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