1 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 2 3 PETSC_EXTERN PetscErrorCode PetscDrawImageSave(const char[],const char[],unsigned char[][3],unsigned int,unsigned int,const unsigned char[]); 4 PETSC_EXTERN PetscErrorCode PetscDrawMovieSave(const char[],PetscInt,const char[],PetscInt,const char[]); 5 PETSC_EXTERN PetscErrorCode PetscDrawImageCheckFormat(const char *[]); 6 PETSC_EXTERN PetscErrorCode PetscDrawMovieCheckFormat(const char *[]); 7 8 /* 9 Code to write images in PPM format 10 */ 11 PETSC_EXTERN PetscErrorCode PetscDrawImageSavePPM(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 12 { 13 int fd; 14 char header[32]; 15 size_t hdrlen; 16 unsigned char *rgb; 17 18 PetscFunctionBegin; 19 PetscValidCharPointer(filename,1); 20 if (palette) PetscValidCharPointer(palette,2); 21 PetscValidCharPointer(pixels,5); 22 /* map pixels to RGB colors */ 23 if (palette) { 24 int k,p,n = (int)(w*h); 25 const unsigned char *colordef; 26 PetscCall(PetscMalloc1(3*w*h,&rgb)); 27 for (k=p=0; k<n; k++) { 28 colordef = palette[pixels[k]]; 29 rgb[p++] = colordef[0]; 30 rgb[p++] = colordef[1]; 31 rgb[p++] = colordef[2]; 32 } 33 } else { /* assume pixels are RGB colors */ 34 rgb = (unsigned char*)pixels; 35 } 36 /* open file and write PPM header */ 37 PetscCall(PetscBinaryOpen(filename,FILE_MODE_WRITE,&fd)); 38 PetscCall(PetscSNPrintf(header,sizeof(header),"P6\n%d %d\n255\n%c",(int)w,(int)h,'\0')); 39 PetscCall(PetscStrlen(header,&hdrlen)); 40 PetscCall(PetscBinaryWrite(fd,header,hdrlen,PETSC_CHAR)); 41 /* write image data and close file */ 42 PetscCall(PetscBinaryWrite(fd,rgb,3*w*h,PETSC_CHAR)); 43 PetscCall(PetscBinaryClose(fd)); 44 if (palette) PetscCall(PetscFree(rgb)); 45 PetscFunctionReturn(0); 46 } 47 48 static PetscErrorCode PetscDrawImageSave_PPM(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 49 { return PetscDrawImageSavePPM(filename,palette,w,h,pixels); } 50 51 /* 52 Code to write images in PNG format 53 */ 54 #if defined(PETSC_HAVE_LIBPNG) 55 56 #include <png.h> 57 58 #if defined(PNG_SETJMP_SUPPORTED) 59 # ifndef png_jmpbuf 60 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) 61 # endif 62 #endif 63 64 PETSC_EXTERN PetscErrorCode PetscDrawImageSavePNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 65 { 66 FILE *fp; 67 png_struct *png_ptr; 68 png_info *info_ptr; 69 unsigned int row, stride = palette ? w : 3*w; 70 71 PetscFunctionBegin; 72 PetscValidCharPointer(filename,1); 73 if (palette) PetscValidCharPointer(palette,2); 74 PetscValidCharPointer(pixels,5); 75 76 /* open file and create libpng structures */ 77 PetscCall(PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp)); 78 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); 79 PetscCheck(png_ptr,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot create PNG context"); 80 info_ptr = png_create_info_struct(png_ptr); 81 PetscCheck(info_ptr,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot create PNG context"); 82 83 /* setup libpng error handling */ 84 #if defined(PNG_SETJMP_SUPPORTED) 85 if (setjmp(png_jmpbuf(png_ptr))) { 86 png_destroy_write_struct(&png_ptr,&info_ptr); 87 (void)PetscFClose(PETSC_COMM_SELF,fp); 88 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing PNG file %s",filename); 89 } 90 #endif 91 92 /* setup PNG image metadata */ 93 png_init_io(png_ptr, fp); 94 png_set_IHDR(png_ptr, info_ptr, w, h, /*depth*/8, 95 palette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB, 96 PNG_INTERLACE_NONE, 97 PNG_COMPRESSION_TYPE_DEFAULT, 98 PNG_FILTER_TYPE_DEFAULT); 99 if (palette) 100 png_set_PLTE(png_ptr, info_ptr, (png_color*)palette, 256); 101 102 /* write PNG image header and data */ 103 png_write_info(png_ptr, info_ptr); 104 for (row = 0; row < h; row++) 105 png_write_row(png_ptr, pixels + row*stride); 106 png_write_end(png_ptr, NULL); 107 108 /* destroy libpng structures and close file */ 109 png_destroy_write_struct(&png_ptr, &info_ptr); 110 PetscCall(PetscFClose(PETSC_COMM_SELF,fp)); 111 PetscFunctionReturn(0); 112 } 113 114 static PetscErrorCode PetscDrawImageSave_PNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 115 { return PetscDrawImageSavePNG(filename,palette,w,h,pixels); } 116 117 #endif/*!PETSC_HAVE_LIBPNG*/ 118 119 /* 120 Code to write images in GIF format 121 */ 122 #if defined(PETSC_HAVE_GIFLIB) 123 124 #include <gif_lib.h> 125 126 #if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5 127 #define GifMakeMapObject MakeMapObject 128 #define GifFreeMapObject FreeMapObject 129 #define EGifOpenFileName(n,b,err) EGifOpenFileName(n,b) 130 #define EGifOpenFileHandle(h,err) EGifOpenFileName(h) 131 #define EGifCloseFile(f,err) EGifCloseFile(f) 132 #define DGifOpenFileName(n,err) DGifOpenFileName(n) 133 #define DGifOpenFileHandle(h,err) DGifOpenFileName(h) 134 #define DGifCloseFile(f,err) DGifCloseFile(f) 135 #endif 136 137 PETSC_EXTERN PetscErrorCode PetscDrawImageSaveGIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 138 { 139 int Row; 140 int Width = (int)w; 141 int Height = (int)h; 142 int ColorRes = 8; 143 int ColorCount = 256; 144 ColorMapObject *GifCMap = NULL; 145 GifFileType *GifFile = NULL; 146 # define SETERRGIF(msg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg", GIF file: %s",filename) 147 # define PetscCallGIF(msg,...) do { \ 148 int Error = __VA_ARGS__; \ 149 if (PetscUnlikely(Error != GIF_OK)) SETERRGIF(msg); \ 150 } while (0) 151 152 PetscFunctionBegin; 153 PetscValidCharPointer(filename,1); 154 PetscValidCharPointer(palette,2); 155 PetscValidCharPointer(pixels,5); 156 157 GifCMap = GifMakeMapObject(ColorCount, (GifColorType*)palette); if (!GifCMap) SETERRGIF("Allocating colormap"); 158 GifFile = EGifOpenFileName(filename, 0, NULL); if (!GifFile) SETERRGIF("Opening"); 159 PetscCallGIF("Writing screen descriptor", EGifPutScreenDesc(GifFile, Width, Height, ColorRes, 0, GifCMap)); 160 PetscCallGIF("Writing image descriptor", EGifPutImageDesc(GifFile, 0, 0, Width, Height, 0, NULL)); 161 for (Row = 0; Row < Height; Row++) { 162 PetscCallGIF("Writing image pixels", EGifPutLine(GifFile, (GifPixelType*)pixels + Row*Width, Width)); 163 } 164 PetscCallGIF("Closing", EGifCloseFile(GifFile, NULL)); 165 GifFreeMapObject(GifCMap); GifCMap = NULL; 166 167 # undef SETERRGIF 168 # undef CHKERRGIF 169 PetscFunctionReturn(0); 170 } 171 172 static PetscErrorCode PetscDrawImageSave_GIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 173 { return PetscDrawImageSaveGIF(filename,palette,w,h,pixels); } 174 175 PETSC_EXTERN PetscErrorCode PetscDrawMovieSaveGIF(const char pattern[],PetscInt count,const char movie[]) 176 { 177 int i,j,Row; 178 char image[PETSC_MAX_PATH_LEN]; 179 GifFileType *GifMovie = NULL; 180 GifFileType *GifImage = NULL; 181 # define SETERRGIF(msg,fn) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg" GIF file %s",fn) 182 183 PetscFunctionBegin; 184 PetscValidCharPointer(pattern,1); 185 PetscValidCharPointer(movie,3); 186 if (count < 1) PetscFunctionReturn(0); 187 188 for (i = 0; i < count; i++) { 189 PetscCall(PetscSNPrintf(image,sizeof(image),pattern,(int)i)); 190 /* open and read image file */ 191 if ((GifImage = DGifOpenFileName(image, NULL)) == NULL) SETERRGIF("Opening input",image); 192 if (DGifSlurp(GifImage) != GIF_OK) SETERRGIF("Reading input",image); 193 /* open movie file and write header */ 194 if (i == 0) { 195 if ((GifMovie = EGifOpenFileName(movie, 0, NULL)) == NULL) SETERRGIF("Opening output",movie); 196 if (EGifPutScreenDesc(GifMovie, 197 GifImage->SWidth, 198 GifImage->SHeight, 199 GifImage->SColorResolution, 200 GifImage->SBackGroundColor, 201 GifImage->SColorMap) != GIF_OK) SETERRGIF("Writing screen descriptor,",movie); 202 } 203 /* loop over all frames in image */ 204 for (j = 0; j < GifImage->ImageCount; j++) { 205 SavedImage *sp = &GifImage->SavedImages[j]; 206 GifImageDesc *GifFrame = &sp->ImageDesc; 207 ColorMapObject *FrameColorMap = GifFrame->ColorMap ? GifFrame->ColorMap : GifImage->SColorMap; 208 if (GifMovie->SColorMap && GifMovie->SColorMap->ColorCount == FrameColorMap->ColorCount && 209 !memcmp(GifMovie->SColorMap->Colors,FrameColorMap->Colors, 210 (size_t)FrameColorMap->ColorCount*sizeof(GifColorType))) 211 FrameColorMap = NULL; 212 /* add frame to movie */ 213 if (EGifPutImageDesc(GifMovie, 214 GifFrame->Left, 215 GifFrame->Top, 216 GifFrame->Width, 217 GifFrame->Height, 218 GifFrame->Interlace, 219 FrameColorMap) != GIF_OK) SETERRGIF("Writing image descriptor,",movie); 220 for (Row = 0; Row < GifFrame->Height; Row++) { 221 if (EGifPutLine(GifMovie, 222 sp->RasterBits + Row * GifFrame->Width, 223 GifFrame->Width) != GIF_OK) SETERRGIF("Writing image pixels,",movie); 224 } 225 } 226 if (DGifCloseFile(GifImage, NULL) != GIF_OK) SETERRGIF("Closing input",image); 227 } 228 if (EGifCloseFile(GifMovie, NULL) != GIF_OK) SETERRGIF("Closing output",movie); 229 230 # undef SETERRGIF 231 PetscFunctionReturn(0); 232 } 233 234 #endif/*!PETSC_HAVE_GIFLIB*/ 235 236 /* 237 Code to write images in JPEG format 238 */ 239 #if defined(PETSC_HAVE_LIBJPEG) 240 241 #include <jpeglib.h> 242 243 #if defined(PETSC_HAVE_SETJMP_H) 244 #include <setjmp.h> 245 static jmp_buf petsc_jpeg_jumpbuf; 246 static void petsc_jpeg_error_longjmp (j_common_ptr cinfo) { (void)cinfo; longjmp(petsc_jpeg_jumpbuf,1); } 247 #endif 248 249 PETSC_EXTERN PetscErrorCode PetscDrawImageSaveJPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 250 { 251 unsigned char *rgbpixels; 252 FILE *fp; 253 struct jpeg_compress_struct cinfo; 254 struct jpeg_error_mgr jerr; 255 256 PetscFunctionBegin; 257 PetscValidCharPointer(filename,1); 258 if (palette) PetscValidCharPointer(palette,2); 259 PetscValidCharPointer(pixels,5); 260 /* map pixels to RGB colors */ 261 if (palette) { 262 int k,p,n = (int)(w*h); 263 const unsigned char *colordef; 264 PetscCall(PetscMalloc1(3*w*h,&rgbpixels)); 265 for (k=p=0; k<n; k++) { 266 colordef = palette[pixels[k]]; 267 rgbpixels[p++] = colordef[0]; 268 rgbpixels[p++] = colordef[1]; 269 rgbpixels[p++] = colordef[2]; 270 } 271 } else { /* assume pixels are RGB colors */ 272 rgbpixels = (unsigned char*)pixels; 273 } 274 PetscCall(PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp)); 275 276 cinfo.err = jpeg_std_error(&jerr); 277 #if defined(PETSC_HAVE_SETJMP_H) 278 jerr.error_exit = petsc_jpeg_error_longjmp; 279 if (setjmp(petsc_jpeg_jumpbuf)) { 280 char message[JMSG_LENGTH_MAX]; 281 jerr.format_message((j_common_ptr)&cinfo,message); 282 jpeg_destroy_compress(&cinfo); 283 (void)PetscFClose(PETSC_COMM_SELF,fp); 284 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing JPEG file %s\n%s",filename,message); 285 } 286 #endif 287 jpeg_create_compress(&cinfo); 288 jpeg_stdio_dest(&cinfo,fp); 289 cinfo.image_width = w; 290 cinfo.image_height = h; 291 cinfo.input_components = 3; 292 cinfo.in_color_space = JCS_RGB; 293 jpeg_set_defaults(&cinfo); 294 jpeg_start_compress(&cinfo,TRUE); 295 while (cinfo.next_scanline < cinfo.image_height) { 296 unsigned char *rowptr = rgbpixels + cinfo.next_scanline * 3*w; 297 (void)jpeg_write_scanlines(&cinfo,&rowptr,1); 298 } 299 jpeg_finish_compress(&cinfo); 300 jpeg_destroy_compress(&cinfo); 301 302 PetscCall(PetscFClose(PETSC_COMM_SELF,fp)); 303 if (palette) PetscCall(PetscFree(rgbpixels)); 304 PetscFunctionReturn(0); 305 } 306 307 static PetscErrorCode PetscDrawImageSave_JPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 308 { return PetscDrawImageSaveJPG(filename,palette,w,h,pixels); } 309 310 #endif/*!PETSC_HAVE_LIBJPEG*/ 311 312 static struct { 313 const char *extension; 314 PetscErrorCode (*SaveImage)(const char[],unsigned char[][3],unsigned int,unsigned int,const unsigned char[]); 315 } PetscDrawImageSaveTable[] = { 316 #if defined(PETSC_HAVE_LIBPNG) 317 {".png", PetscDrawImageSave_PNG}, 318 #endif 319 #if defined(PETSC_HAVE_GIFLIB) 320 {".gif", PetscDrawImageSave_GIF}, 321 #endif 322 #if defined(PETSC_HAVE_LIBJPEG) 323 {".jpg", PetscDrawImageSave_JPG}, 324 #endif 325 {".ppm", PetscDrawImageSave_PPM} 326 }; 327 328 PetscErrorCode PetscDrawImageCheckFormat(const char *ext[]) 329 { 330 size_t k; 331 PetscBool match = PETSC_FALSE; 332 333 PetscFunctionBegin; 334 /* if extension is empty, return default format to caller */ 335 PetscValidPointer(ext,1); 336 if (!*ext || !**ext) { 337 *ext = PetscDrawImageSaveTable[0].extension; 338 PetscFunctionReturn(0); 339 } 340 /* check the extension matches a supported format */ 341 PetscValidCharPointer(*ext,1); 342 for (k=0; k<PETSC_STATIC_ARRAY_LENGTH(PetscDrawImageSaveTable); k++) { 343 PetscCall(PetscStrcasecmp(*ext,PetscDrawImageSaveTable[k].extension,&match)); 344 if (match && PetscDrawImageSaveTable[k].SaveImage) PetscFunctionReturn(0); 345 } 346 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Image extension %s not supported, use .ppm or see PetscDrawSetSave() for what ./configure option you may need",*ext); 347 } 348 349 PetscErrorCode PetscDrawImageSave(const char basename[],const char ext[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 350 { 351 size_t k; 352 PetscBool match = PETSC_FALSE; 353 char filename[PETSC_MAX_PATH_LEN]; 354 355 PetscFunctionBegin; 356 PetscValidCharPointer(basename,1); 357 if (ext) PetscValidCharPointer(ext,2); 358 if (palette) PetscValidPointer(palette,3); 359 PetscValidCharPointer(pixels,6); 360 361 PetscCall(PetscDrawImageCheckFormat(&ext)); 362 PetscCall(PetscSNPrintf(filename,sizeof(filename),"%s%s",basename,ext)); 363 for (k=0; k<PETSC_STATIC_ARRAY_LENGTH(PetscDrawImageSaveTable); k++) { 364 PetscCall(PetscStrcasecmp(ext,PetscDrawImageSaveTable[k].extension,&match)); 365 if (match && PetscDrawImageSaveTable[k].SaveImage) { 366 PetscCall(PetscDrawImageSaveTable[k].SaveImage(filename,palette,w,h,pixels)); 367 PetscFunctionReturn(0); 368 } 369 } 370 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Image extension %s not supported, use .ppm",ext); 371 } 372 373 PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[]) 374 { 375 PetscFunctionBegin; 376 PetscValidPointer(ext,1); 377 if (!*ext || !**ext) *ext = ".m4v"; 378 PetscFunctionReturn(0); 379 } 380 381 PetscErrorCode PetscDrawMovieSave(const char basename[],PetscInt count,const char imext[],PetscInt fps,const char mvext[]) 382 { 383 char input[PETSC_MAX_PATH_LEN]; 384 char output[PETSC_MAX_PATH_LEN]; 385 PetscBool gifinput; 386 387 PetscFunctionBegin; 388 PetscValidCharPointer(basename,1); 389 PetscValidCharPointer(imext,3); 390 if (mvext) PetscValidCharPointer(mvext,5); 391 if (count < 1) PetscFunctionReturn(0); 392 393 PetscCall(PetscStrcasecmp(imext,".gif",&gifinput)); 394 PetscCall(PetscDrawMovieCheckFormat(&mvext)); 395 PetscCall(PetscSNPrintf(input,sizeof(input),"%s/%s_%%d%s",basename,basename,imext)); 396 PetscCall(PetscSNPrintf(output,sizeof(output),"%s%s",basename,mvext)); 397 398 /* use GIFLIB to generate an intermediate GIF animation */ 399 #if defined(PETSC_HAVE_GIFLIB) 400 if (gifinput) { 401 char gifmovie[PETSC_MAX_PATH_LEN]; 402 PetscCall(PetscSNPrintf(gifmovie,sizeof(gifmovie),"%s/%s_movie.gif",basename,basename)); 403 PetscCall(PetscDrawMovieSaveGIF(input,count,gifmovie)); 404 PetscCall(PetscStrcpy(input,gifmovie)); 405 } 406 #endif 407 408 /* use FFmpeg to generate a movie */ 409 #if defined(PETSC_HAVE_POPEN) 410 { 411 FILE *fd; 412 char options[64] = "-loglevel error -y", extraopts[32] = "", framerate[24] = ""; 413 char command[sizeof(options)+sizeof(extraopts)+sizeof(framerate)+PETSC_MAX_PATH_LEN*2]; 414 if (fps > 0) PetscCall(PetscSNPrintf(framerate,sizeof(framerate),"-r %d",(int)fps)); 415 if (gifinput) { 416 PetscCall(PetscStrlcat(options," -f gif",sizeof(options))); 417 PetscCall(PetscSNPrintf(extraopts,sizeof(extraopts)," -default_delay %d",(fps > 0) ? 100/(int)fps : 4)); 418 } else { 419 PetscCall(PetscStrlcat(options," -f image2",sizeof(options))); 420 if (fps > 0) PetscCall(PetscSNPrintf(extraopts,sizeof(extraopts)," -framerate %d",(int)fps)); 421 } 422 if (extraopts[0]) PetscCall(PetscStrlcat(options,extraopts,sizeof(options))); 423 PetscCall(PetscSNPrintf(command,sizeof(command),"ffmpeg %s -i \"%s\" %s \"%s\"",options,input,framerate,output)); 424 PetscCall(PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd)); 425 PetscCall(PetscPClose(PETSC_COMM_SELF,fd)); 426 } 427 #endif 428 PetscFunctionReturn(0); 429 } 430