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 CHKERRQ(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 CHKERRQ(PetscBinaryOpen(filename,FILE_MODE_WRITE,&fd)); 38 CHKERRQ(PetscSNPrintf(header,sizeof(header),"P6\n%d %d\n255\n%c",(int)w,(int)h,'\0')); 39 CHKERRQ(PetscStrlen(header,&hdrlen)); 40 CHKERRQ(PetscBinaryWrite(fd,header,hdrlen,PETSC_CHAR)); 41 /* write image data and close file */ 42 CHKERRQ(PetscBinaryWrite(fd,rgb,3*w*h,PETSC_CHAR)); 43 CHKERRQ(PetscBinaryClose(fd)); 44 if (palette) CHKERRQ(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 CHKERRQ(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 CHKERRQ(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, Error; 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 CHKERRGIF(msg) do {if (Error != GIF_OK) SETERRGIF(msg);} while (0) 148 149 PetscFunctionBegin; 150 PetscValidCharPointer(filename,1); 151 PetscValidCharPointer(palette,2); 152 PetscValidCharPointer(pixels,5); 153 154 GifCMap = GifMakeMapObject(ColorCount, (GifColorType*)palette); if (!GifCMap) SETERRGIF("Allocating colormap"); 155 GifFile = EGifOpenFileName(filename, 0, NULL); if (!GifFile) SETERRGIF("Opening"); 156 Error = EGifPutScreenDesc(GifFile, Width, Height, ColorRes, 0, GifCMap); CHKERRGIF("Writing screen descriptor"); 157 Error = EGifPutImageDesc(GifFile, 0, 0, Width, Height, 0, NULL); CHKERRGIF("Writing image descriptor"); 158 for (Row = 0; Row < Height; Row++) { 159 Error = EGifPutLine(GifFile, (GifPixelType*)pixels + Row*Width, Width); CHKERRGIF("Writing image pixels"); 160 } 161 Error = EGifCloseFile(GifFile, NULL); CHKERRGIF("Closing"); 162 GifFreeMapObject(GifCMap); GifCMap = NULL; 163 164 # undef SETERRGIF 165 # undef CHKERRGIF 166 PetscFunctionReturn(0); 167 } 168 169 static PetscErrorCode PetscDrawImageSave_GIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 170 { return PetscDrawImageSaveGIF(filename,palette,w,h,pixels); } 171 172 PETSC_EXTERN PetscErrorCode PetscDrawMovieSaveGIF(const char pattern[],PetscInt count,const char movie[]) 173 { 174 int i,j,Row; 175 char image[PETSC_MAX_PATH_LEN]; 176 GifFileType *GifMovie = NULL; 177 GifFileType *GifImage = NULL; 178 # define SETERRGIF(msg,fn) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg" GIF file %s",fn) 179 180 PetscFunctionBegin; 181 PetscValidCharPointer(pattern,1); 182 PetscValidCharPointer(movie,3); 183 if (count < 1) PetscFunctionReturn(0); 184 185 for (i = 0; i < count; i++) { 186 CHKERRQ(PetscSNPrintf(image,sizeof(image),pattern,(int)i)); 187 /* open and read image file */ 188 if ((GifImage = DGifOpenFileName(image, NULL)) == NULL) SETERRGIF("Opening input",image); 189 if (DGifSlurp(GifImage) != GIF_OK) SETERRGIF("Reading input",image); 190 /* open movie file and write header */ 191 if (i == 0) { 192 if ((GifMovie = EGifOpenFileName(movie, 0, NULL)) == NULL) SETERRGIF("Opening output",movie); 193 if (EGifPutScreenDesc(GifMovie, 194 GifImage->SWidth, 195 GifImage->SHeight, 196 GifImage->SColorResolution, 197 GifImage->SBackGroundColor, 198 GifImage->SColorMap) != GIF_OK) SETERRGIF("Writing screen descriptor,",movie); 199 } 200 /* loop over all frames in image */ 201 for (j = 0; j < GifImage->ImageCount; j++) { 202 SavedImage *sp = &GifImage->SavedImages[j]; 203 GifImageDesc *GifFrame = &sp->ImageDesc; 204 ColorMapObject *FrameColorMap = GifFrame->ColorMap ? GifFrame->ColorMap : GifImage->SColorMap; 205 if (GifMovie->SColorMap && GifMovie->SColorMap->ColorCount == FrameColorMap->ColorCount && 206 !memcmp(GifMovie->SColorMap->Colors,FrameColorMap->Colors, 207 (size_t)FrameColorMap->ColorCount*sizeof(GifColorType))) 208 FrameColorMap = NULL; 209 /* add frame to movie */ 210 if (EGifPutImageDesc(GifMovie, 211 GifFrame->Left, 212 GifFrame->Top, 213 GifFrame->Width, 214 GifFrame->Height, 215 GifFrame->Interlace, 216 FrameColorMap) != GIF_OK) SETERRGIF("Writing image descriptor,",movie); 217 for (Row = 0; Row < GifFrame->Height; Row++) { 218 if (EGifPutLine(GifMovie, 219 sp->RasterBits + Row * GifFrame->Width, 220 GifFrame->Width) != GIF_OK) SETERRGIF("Writing image pixels,",movie); 221 } 222 } 223 if (DGifCloseFile(GifImage, NULL) != GIF_OK) SETERRGIF("Closing input",image); 224 } 225 if (EGifCloseFile(GifMovie, NULL) != GIF_OK) SETERRGIF("Closing output",movie); 226 227 # undef SETERRGIF 228 PetscFunctionReturn(0); 229 } 230 231 #endif/*!PETSC_HAVE_GIFLIB*/ 232 233 /* 234 Code to write images in JPEG format 235 */ 236 #if defined(PETSC_HAVE_LIBJPEG) 237 238 #include <jpeglib.h> 239 240 #if defined(PETSC_HAVE_SETJMP_H) 241 #include <setjmp.h> 242 static jmp_buf petsc_jpeg_jumpbuf; 243 static void petsc_jpeg_error_longjmp (j_common_ptr cinfo) { (void)cinfo; longjmp(petsc_jpeg_jumpbuf,1); } 244 #endif 245 246 PETSC_EXTERN PetscErrorCode PetscDrawImageSaveJPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 247 { 248 unsigned char *rgbpixels; 249 FILE *fp; 250 struct jpeg_compress_struct cinfo; 251 struct jpeg_error_mgr jerr; 252 253 PetscFunctionBegin; 254 PetscValidCharPointer(filename,1); 255 if (palette) PetscValidCharPointer(palette,2); 256 PetscValidCharPointer(pixels,5); 257 /* map pixels to RGB colors */ 258 if (palette) { 259 int k,p,n = (int)(w*h); 260 const unsigned char *colordef; 261 CHKERRQ(PetscMalloc1(3*w*h,&rgbpixels)); 262 for (k=p=0; k<n; k++) { 263 colordef = palette[pixels[k]]; 264 rgbpixels[p++] = colordef[0]; 265 rgbpixels[p++] = colordef[1]; 266 rgbpixels[p++] = colordef[2]; 267 } 268 } else { /* assume pixels are RGB colors */ 269 rgbpixels = (unsigned char*)pixels; 270 } 271 CHKERRQ(PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp)); 272 273 cinfo.err = jpeg_std_error(&jerr); 274 #if defined(PETSC_HAVE_SETJMP_H) 275 jerr.error_exit = petsc_jpeg_error_longjmp; 276 if (setjmp(petsc_jpeg_jumpbuf)) { 277 char message[JMSG_LENGTH_MAX]; 278 jerr.format_message((j_common_ptr)&cinfo,message); 279 jpeg_destroy_compress(&cinfo); 280 (void)PetscFClose(PETSC_COMM_SELF,fp); 281 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing JPEG file %s\n%s",filename,message); 282 } 283 #endif 284 jpeg_create_compress(&cinfo); 285 jpeg_stdio_dest(&cinfo,fp); 286 cinfo.image_width = w; 287 cinfo.image_height = h; 288 cinfo.input_components = 3; 289 cinfo.in_color_space = JCS_RGB; 290 jpeg_set_defaults(&cinfo); 291 jpeg_start_compress(&cinfo,TRUE); 292 while (cinfo.next_scanline < cinfo.image_height) { 293 unsigned char *rowptr = rgbpixels + cinfo.next_scanline * 3*w; 294 (void)jpeg_write_scanlines(&cinfo,&rowptr,1); 295 } 296 jpeg_finish_compress(&cinfo); 297 jpeg_destroy_compress(&cinfo); 298 299 CHKERRQ(PetscFClose(PETSC_COMM_SELF,fp)); 300 if (palette) CHKERRQ(PetscFree(rgbpixels)); 301 PetscFunctionReturn(0); 302 } 303 304 static PetscErrorCode PetscDrawImageSave_JPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 305 { return PetscDrawImageSaveJPG(filename,palette,w,h,pixels); } 306 307 #endif/*!PETSC_HAVE_LIBJPEG*/ 308 309 static struct { 310 const char *extension; 311 PetscErrorCode (*SaveImage)(const char[],unsigned char[][3],unsigned int,unsigned int,const unsigned char[]); 312 } PetscDrawImageSaveTable[] = { 313 #if defined(PETSC_HAVE_LIBPNG) 314 {".png", PetscDrawImageSave_PNG}, 315 #endif 316 #if defined(PETSC_HAVE_GIFLIB) 317 {".gif", PetscDrawImageSave_GIF}, 318 #endif 319 #if defined(PETSC_HAVE_LIBJPEG) 320 {".jpg", PetscDrawImageSave_JPG}, 321 #endif 322 {".ppm", PetscDrawImageSave_PPM} 323 }; 324 325 PetscErrorCode PetscDrawImageCheckFormat(const char *ext[]) 326 { 327 size_t k; 328 PetscBool match = PETSC_FALSE; 329 330 PetscFunctionBegin; 331 /* if extension is empty, return default format to caller */ 332 PetscValidPointer(ext,1); 333 if (!*ext || !**ext) { 334 *ext = PetscDrawImageSaveTable[0].extension; 335 PetscFunctionReturn(0); 336 } 337 /* check the extension matches a supported format */ 338 PetscValidCharPointer(*ext,1); 339 for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) { 340 CHKERRQ(PetscStrcasecmp(*ext,PetscDrawImageSaveTable[k].extension,&match)); 341 if (match && PetscDrawImageSaveTable[k].SaveImage) PetscFunctionReturn(0); 342 } 343 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); 344 } 345 346 PetscErrorCode PetscDrawImageSave(const char basename[],const char ext[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[]) 347 { 348 size_t k; 349 PetscBool match = PETSC_FALSE; 350 char filename[PETSC_MAX_PATH_LEN]; 351 352 PetscFunctionBegin; 353 PetscValidCharPointer(basename,1); 354 if (ext) PetscValidCharPointer(ext,2); 355 if (palette) PetscValidPointer(palette,3); 356 PetscValidCharPointer(pixels,6); 357 358 CHKERRQ(PetscDrawImageCheckFormat(&ext)); 359 CHKERRQ(PetscSNPrintf(filename,sizeof(filename),"%s%s",basename,ext)); 360 for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) { 361 CHKERRQ(PetscStrcasecmp(ext,PetscDrawImageSaveTable[k].extension,&match)); 362 if (match && PetscDrawImageSaveTable[k].SaveImage) { 363 CHKERRQ(PetscDrawImageSaveTable[k].SaveImage(filename,palette,w,h,pixels)); 364 PetscFunctionReturn(0); 365 } 366 } 367 SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Image extension %s not supported, use .ppm",ext); 368 } 369 370 PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[]) 371 { 372 PetscFunctionBegin; 373 PetscValidPointer(ext,1); 374 if (!*ext || !**ext) *ext = ".m4v"; 375 PetscFunctionReturn(0); 376 } 377 378 PetscErrorCode PetscDrawMovieSave(const char basename[],PetscInt count,const char imext[],PetscInt fps,const char mvext[]) 379 { 380 char input[PETSC_MAX_PATH_LEN]; 381 char output[PETSC_MAX_PATH_LEN]; 382 PetscBool gifinput; 383 384 PetscFunctionBegin; 385 PetscValidCharPointer(basename,1); 386 PetscValidCharPointer(imext,3); 387 if (mvext) PetscValidCharPointer(mvext,5); 388 if (count < 1) PetscFunctionReturn(0); 389 390 CHKERRQ(PetscStrcasecmp(imext,".gif",&gifinput)); 391 CHKERRQ(PetscDrawMovieCheckFormat(&mvext)); 392 CHKERRQ(PetscSNPrintf(input,sizeof(input),"%s/%s_%%d%s",basename,basename,imext)); 393 CHKERRQ(PetscSNPrintf(output,sizeof(output),"%s%s",basename,mvext)); 394 395 /* use GIFLIB to generate an intermediate GIF animation */ 396 #if defined(PETSC_HAVE_GIFLIB) 397 if (gifinput) { 398 char gifmovie[PETSC_MAX_PATH_LEN]; 399 CHKERRQ(PetscSNPrintf(gifmovie,sizeof(gifmovie),"%s/%s_movie.gif",basename,basename)); 400 CHKERRQ(PetscDrawMovieSaveGIF(input,count,gifmovie)); 401 CHKERRQ(PetscStrcpy(input,gifmovie)); 402 } 403 #endif 404 405 /* use FFmpeg to generate a movie */ 406 #if defined(PETSC_HAVE_POPEN) 407 { 408 FILE *fd; 409 char options[64] = "-loglevel error -y", extraopts[32] = "", framerate[24] = ""; 410 char command[sizeof(options)+sizeof(extraopts)+sizeof(framerate)+PETSC_MAX_PATH_LEN*2]; 411 if (fps > 0) CHKERRQ(PetscSNPrintf(framerate,sizeof(framerate),"-r %d",(int)fps)); 412 if (gifinput) { 413 CHKERRQ(PetscStrlcat(options," -f gif",sizeof(options))); 414 CHKERRQ(PetscSNPrintf(extraopts,sizeof(extraopts)," -default_delay %d",(fps > 0) ? 100/(int)fps : 4)); 415 } else { 416 CHKERRQ(PetscStrlcat(options," -f image2",sizeof(options))); 417 if (fps > 0) CHKERRQ(PetscSNPrintf(extraopts,sizeof(extraopts)," -framerate %d",(int)fps)); 418 } 419 if (extraopts[0]) CHKERRQ(PetscStrlcat(options,extraopts,sizeof(options))); 420 CHKERRQ(PetscSNPrintf(command,sizeof(command),"ffmpeg %s -i \"%s\" %s \"%s\"",options,input,framerate,output)); 421 CHKERRQ(PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd)); 422 CHKERRQ(PetscPClose(PETSC_COMM_SELF,fd)); 423 } 424 #endif 425 PetscFunctionReturn(0); 426 } 427