Actual source code: image.c

  1: #include <petsc/private/petscimpl.h>

  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 *[]);

  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;

 21:   /* map pixels to RGB colors */
 22:   if (palette) {
 23:     int k,p,n = (int)(w*h);
 24:     const unsigned char *colordef;
 25:     PetscMalloc1(3*w*h,&rgb);
 26:     for (k=p=0; k<n; k++) {
 27:       colordef = palette[pixels[k]];
 28:       rgb[p++] = colordef[0];
 29:       rgb[p++] = colordef[1];
 30:       rgb[p++] = colordef[2];
 31:     }
 32:   } else { /* assume pixels are RGB colors */
 33:     rgb = (unsigned char*)pixels;
 34:   }
 35:   /* open file and write PPM header */
 36:   PetscBinaryOpen(filename,FILE_MODE_WRITE,&fd);
 37:   PetscSNPrintf(header,sizeof(header),"P6\n%d %d\n255\n%c",(int)w,(int)h,'\0');
 38:   PetscStrlen(header,&hdrlen);
 39:   PetscBinaryWrite(fd,header,hdrlen,PETSC_CHAR);
 40:   /* write image data and close file */
 41:   PetscBinaryWrite(fd,rgb,3*w*h,PETSC_CHAR);
 42:   PetscBinaryClose(fd);
 43:   if (palette) PetscFree(rgb);
 44:   return 0;
 45: }

 47: static PetscErrorCode PetscDrawImageSave_PPM(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
 48: { return PetscDrawImageSavePPM(filename,palette,w,h,pixels); }

 50: /*
 51:    Code to write images in PNG format
 52: */
 53: #if defined(PETSC_HAVE_LIBPNG)

 55: #include <png.h>

 57: #if defined(PNG_SETJMP_SUPPORTED)
 58: # ifndef png_jmpbuf
 59: #   define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
 60: # endif
 61: #endif

 63: PETSC_EXTERN PetscErrorCode PetscDrawImageSavePNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
 64: {
 65:   FILE           *fp;
 66:   png_struct     *png_ptr;
 67:   png_info       *info_ptr;
 68:   unsigned int   row, stride = palette ? w : 3*w;


 74:   /* open file and create libpng structures */
 75:   PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp);
 76:   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
 78:   info_ptr = png_create_info_struct(png_ptr);

 81:   /* setup libpng error handling */
 82: #if defined(PNG_SETJMP_SUPPORTED)
 83:   if (setjmp(png_jmpbuf(png_ptr))) {
 84:     png_destroy_write_struct(&png_ptr,&info_ptr);
 85:     (void)PetscFClose(PETSC_COMM_SELF,fp);
 86:     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing PNG file %s",filename);
 87:   }
 88: #endif

 90:   /* setup PNG image metadata */
 91:   png_init_io(png_ptr, fp);
 92:   png_set_IHDR(png_ptr, info_ptr, w, h, /*depth*/8,
 93:                palette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
 94:                PNG_INTERLACE_NONE,
 95:                PNG_COMPRESSION_TYPE_DEFAULT,
 96:                PNG_FILTER_TYPE_DEFAULT);
 97:   if (palette)
 98:     png_set_PLTE(png_ptr, info_ptr, (png_color*)palette, 256);

100:   /* write PNG image header and data */
101:   png_write_info(png_ptr, info_ptr);
102:   for (row = 0; row < h; row++)
103:     png_write_row(png_ptr, pixels + row*stride);
104:   png_write_end(png_ptr, NULL);

106:   /* destroy libpng structures and close file */
107:   png_destroy_write_struct(&png_ptr, &info_ptr);
108:   PetscFClose(PETSC_COMM_SELF,fp);
109:   return 0;
110: }

112: static PetscErrorCode PetscDrawImageSave_PNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
113: { return PetscDrawImageSavePNG(filename,palette,w,h,pixels); }

115: #endif/*!PETSC_HAVE_LIBPNG*/

117: /*
118:    Code to write images in GIF format
119: */
120: #if defined(PETSC_HAVE_GIFLIB)

122: #include <gif_lib.h>

124: #if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5
125: #define GifMakeMapObject          MakeMapObject
126: #define GifFreeMapObject          FreeMapObject
127: #define EGifOpenFileName(n,b,err) EGifOpenFileName(n,b)
128: #define EGifOpenFileHandle(h,err) EGifOpenFileName(h)
129: #define EGifCloseFile(f,err)      EGifCloseFile(f)
130: #define DGifOpenFileName(n,err)   DGifOpenFileName(n)
131: #define DGifOpenFileHandle(h,err) DGifOpenFileName(h)
132: #define DGifCloseFile(f,err)      DGifCloseFile(f)
133: #endif

135: PETSC_EXTERN PetscErrorCode PetscDrawImageSaveGIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
136: {
137:   int            Row;
138:   int            Width  = (int)w;
139:   int            Height = (int)h;
140:   int            ColorRes   = 8;
141:   int            ColorCount = 256;
142:   ColorMapObject *GifCMap = NULL;
143:   GifFileType    *GifFile = NULL;
144: # define         SETERRGIF(msg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg", GIF file: %s",filename)
145: # define         PetscCallGIF(msg,...) do {             \
146:     int Error = __VA_ARGS__;                            \
147:     if (PetscUnlikely(Error != GIF_OK)) SETERRGIF(msg); \
148:   } while (0)


154:   GifCMap = GifMakeMapObject(ColorCount, (GifColorType*)palette); if (!GifCMap) SETERRGIF("Allocating colormap");
155:   GifFile = EGifOpenFileName(filename, 0, NULL); if (!GifFile) SETERRGIF("Opening");
156:   "Writing screen descriptor", EGifPutScreenDesc(GifFile, Width, Height, ColorRes, 0, GifCMap);
157:   "Writing image descriptor", EGifPutImageDesc(GifFile, 0, 0, Width, Height, 0, NULL);
158:   for (Row = 0; Row < Height; Row++) {
159:     "Writing image pixels", EGifPutLine(GifFile, (GifPixelType*)pixels + Row*Width, Width);
160:   }
161:   "Closing", EGifCloseFile(GifFile, NULL);
162:   GifFreeMapObject(GifCMap); GifCMap = NULL;

164: # undef SETERRGIF
165: # undef CHKERRGIF
166:   return 0;
167: }

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); }

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)

182:   if (count < 1) return 0;

184:   for (i = 0; i < count; i++) {
185:     PetscSNPrintf(image,sizeof(image),pattern,(int)i);
186:     /* open and read image file */
187:     if ((GifImage = DGifOpenFileName(image, NULL)) == NULL) SETERRGIF("Opening input",image);
188:     if (DGifSlurp(GifImage) != GIF_OK) SETERRGIF("Reading input",image);
189:     /* open movie file and write header */
190:     if (i == 0) {
191:       if ((GifMovie = EGifOpenFileName(movie, 0, NULL)) == NULL) SETERRGIF("Opening output",movie);
192:       if (EGifPutScreenDesc(GifMovie,
193:                             GifImage->SWidth,
194:                             GifImage->SHeight,
195:                             GifImage->SColorResolution,
196:                             GifImage->SBackGroundColor,
197:                             GifImage->SColorMap) != GIF_OK) SETERRGIF("Writing screen descriptor,",movie);
198:     }
199:     /* loop over all frames in image */
200:     for (j = 0; j < GifImage->ImageCount; j++) {
201:       SavedImage *sp = &GifImage->SavedImages[j];
202:       GifImageDesc *GifFrame = &sp->ImageDesc;
203:       ColorMapObject *FrameColorMap = GifFrame->ColorMap ? GifFrame->ColorMap : GifImage->SColorMap;
204:       if (GifMovie->SColorMap && GifMovie->SColorMap->ColorCount == FrameColorMap->ColorCount &&
205:           !memcmp(GifMovie->SColorMap->Colors,FrameColorMap->Colors,
206:                   (size_t)FrameColorMap->ColorCount*sizeof(GifColorType)))
207:         FrameColorMap = NULL;
208:       /* add frame to movie */
209:       if (EGifPutImageDesc(GifMovie,
210:                            GifFrame->Left,
211:                            GifFrame->Top,
212:                            GifFrame->Width,
213:                            GifFrame->Height,
214:                            GifFrame->Interlace,
215:                            FrameColorMap) != GIF_OK) SETERRGIF("Writing image descriptor,",movie);
216:       for (Row = 0; Row < GifFrame->Height; Row++) {
217:         if (EGifPutLine(GifMovie,
218:                         sp->RasterBits + Row * GifFrame->Width,
219:                         GifFrame->Width) != GIF_OK) SETERRGIF("Writing image pixels,",movie);
220:       }
221:     }
222:     if (DGifCloseFile(GifImage, NULL) != GIF_OK) SETERRGIF("Closing input",image);
223:   }
224:   if (EGifCloseFile(GifMovie, NULL) != GIF_OK) SETERRGIF("Closing output",movie);

226: # undef SETERRGIF
227:   return 0;
228: }

230: #endif/*!PETSC_HAVE_GIFLIB*/

232: /*
233:    Code to write images in JPEG format
234: */
235: #if defined(PETSC_HAVE_LIBJPEG)

237: #include <jpeglib.h>

239: #if defined(PETSC_HAVE_SETJMP_H)
240: #include <setjmp.h>
241: static jmp_buf petsc_jpeg_jumpbuf;
242: static void petsc_jpeg_error_longjmp (j_common_ptr cinfo) { (void)cinfo; longjmp(petsc_jpeg_jumpbuf,1); }
243: #endif

245: PETSC_EXTERN PetscErrorCode PetscDrawImageSaveJPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
246: {
247:   unsigned char               *rgbpixels;
248:   FILE                        *fp;
249:   struct jpeg_compress_struct cinfo;
250:   struct jpeg_error_mgr       jerr;

255:   /* map pixels to RGB colors */
256:   if (palette) {
257:     int k,p,n = (int)(w*h);
258:     const unsigned char *colordef;
259:     PetscMalloc1(3*w*h,&rgbpixels);
260:     for (k=p=0; k<n; k++) {
261:       colordef = palette[pixels[k]];
262:       rgbpixels[p++] = colordef[0];
263:       rgbpixels[p++] = colordef[1];
264:       rgbpixels[p++] = colordef[2];
265:     }
266:   } else { /* assume pixels are RGB colors */
267:     rgbpixels = (unsigned char*)pixels;
268:   }
269:   PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp);

271:   cinfo.err = jpeg_std_error(&jerr);
272: #if defined(PETSC_HAVE_SETJMP_H)
273:   jerr.error_exit = petsc_jpeg_error_longjmp;
274:   if (setjmp(petsc_jpeg_jumpbuf)) {
275:     char message[JMSG_LENGTH_MAX];
276:     jerr.format_message((j_common_ptr)&cinfo,message);
277:     jpeg_destroy_compress(&cinfo);
278:     (void)PetscFClose(PETSC_COMM_SELF,fp);
279:     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing JPEG file %s\n%s",filename,message);
280:   }
281: #endif
282:   jpeg_create_compress(&cinfo);
283:   jpeg_stdio_dest(&cinfo,fp);
284:   cinfo.image_width      = w;
285:   cinfo.image_height     = h;
286:   cinfo.input_components = 3;
287:   cinfo.in_color_space   = JCS_RGB;
288:   jpeg_set_defaults(&cinfo);
289:   jpeg_start_compress(&cinfo,TRUE);
290:   while (cinfo.next_scanline < cinfo.image_height) {
291:     unsigned char *rowptr = rgbpixels + cinfo.next_scanline * 3*w;
292:     (void)jpeg_write_scanlines(&cinfo,&rowptr,1);
293:   }
294:   jpeg_finish_compress(&cinfo);
295:   jpeg_destroy_compress(&cinfo);

297:   PetscFClose(PETSC_COMM_SELF,fp);
298:   if (palette) PetscFree(rgbpixels);
299:   return 0;
300: }

302: static PetscErrorCode PetscDrawImageSave_JPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
303: { return PetscDrawImageSaveJPG(filename,palette,w,h,pixels); }

305: #endif/*!PETSC_HAVE_LIBJPEG*/

307: static struct {
308:   const char      *extension;
309:   PetscErrorCode (*SaveImage)(const char[],unsigned char[][3],unsigned int,unsigned int,const unsigned char[]);
310: } PetscDrawImageSaveTable[] = {
311: #if defined(PETSC_HAVE_LIBPNG)
312:   {".png", PetscDrawImageSave_PNG},
313: #endif
314: #if defined(PETSC_HAVE_GIFLIB)
315:   {".gif", PetscDrawImageSave_GIF},
316: #endif
317: #if defined(PETSC_HAVE_LIBJPEG)
318:   {".jpg", PetscDrawImageSave_JPG},
319: #endif
320:   {".ppm", PetscDrawImageSave_PPM}
321: };

323: PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
324: {
325:   size_t         k;
326:   PetscBool      match = PETSC_FALSE;

328:   /* if extension is empty, return default format to caller */
330:   if (!*ext || !**ext) {
331:     *ext = PetscDrawImageSaveTable[0].extension;
332:     return 0;
333:   }
334:   /* check the extension matches a supported format */
336:   for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) {
337:     PetscStrcasecmp(*ext,PetscDrawImageSaveTable[k].extension,&match);
338:     if (match && PetscDrawImageSaveTable[k].SaveImage) return 0;
339:   }
340:   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);
341: }

343: PetscErrorCode PetscDrawImageSave(const char basename[],const char ext[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
344: {
345:   size_t         k;
346:   PetscBool      match = PETSC_FALSE;
347:   char           filename[PETSC_MAX_PATH_LEN];


354:   PetscDrawImageCheckFormat(&ext);
355:   PetscSNPrintf(filename,sizeof(filename),"%s%s",basename,ext);
356:   for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) {
357:     PetscStrcasecmp(ext,PetscDrawImageSaveTable[k].extension,&match);
358:     if (match && PetscDrawImageSaveTable[k].SaveImage) {
359:       PetscDrawImageSaveTable[k].SaveImage(filename,palette,w,h,pixels);
360:       return 0;
361:     }
362:   }
363:   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Image extension %s not supported, use .ppm",ext);
364: }

366: PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
367: {
369:   if (!*ext || !**ext) *ext = ".m4v";
370:   return 0;
371: }

373: PetscErrorCode PetscDrawMovieSave(const char basename[],PetscInt count,const char imext[],PetscInt fps,const char mvext[])
374: {
375:   char           input[PETSC_MAX_PATH_LEN];
376:   char           output[PETSC_MAX_PATH_LEN];
377:   PetscBool      gifinput;

382:   if (count < 1) return 0;

384:   PetscStrcasecmp(imext,".gif",&gifinput);
385:   PetscDrawMovieCheckFormat(&mvext);
386:   PetscSNPrintf(input,sizeof(input),"%s/%s_%%d%s",basename,basename,imext);
387:   PetscSNPrintf(output,sizeof(output),"%s%s",basename,mvext);

389:   /* use GIFLIB to generate an intermediate GIF animation */
390: #if defined(PETSC_HAVE_GIFLIB)
391:   if (gifinput) {
392:     char gifmovie[PETSC_MAX_PATH_LEN];
393:     PetscSNPrintf(gifmovie,sizeof(gifmovie),"%s/%s_movie.gif",basename,basename);
394:     PetscDrawMovieSaveGIF(input,count,gifmovie);
395:     PetscStrcpy(input,gifmovie);
396:   }
397: #endif

399:   /* use FFmpeg to generate a movie */
400: #if defined(PETSC_HAVE_POPEN)
401:   {
402:     FILE *fd;
403:     char options[64] = "-loglevel error -y", extraopts[32] = "", framerate[24] = "";
404:     char command[sizeof(options)+sizeof(extraopts)+sizeof(framerate)+PETSC_MAX_PATH_LEN*2];
405:     if (fps > 0) PetscSNPrintf(framerate,sizeof(framerate),"-r %d",(int)fps);
406:     if (gifinput) {
407:       PetscStrlcat(options," -f gif",sizeof(options));
408:       PetscSNPrintf(extraopts,sizeof(extraopts)," -default_delay %d",(fps > 0) ? 100/(int)fps : 4);
409:     } else {
410:       PetscStrlcat(options," -f image2",sizeof(options));
411:       if (fps > 0) PetscSNPrintf(extraopts,sizeof(extraopts)," -framerate %d",(int)fps);
412:     }
413:     if (extraopts[0]) PetscStrlcat(options,extraopts,sizeof(options));
414:     PetscSNPrintf(command,sizeof(command),"ffmpeg %s -i \"%s\" %s \"%s\"",options,input,framerate,output);
415:     PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd);
416:     PetscPClose(PETSC_COMM_SELF,fd);
417:   }
418: #endif
419:   return 0;
420: }