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: {
 49:   return PetscDrawImageSavePPM(filename, palette, w, h, pixels);
 50: }

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

 57:   #include <png.h>

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

 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;


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

 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

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

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

102:   /* destroy libpng structures and close file */
103:   png_destroy_write_struct(&png_ptr, &info_ptr);
104:   PetscFClose(PETSC_COMM_SELF, fp);
105:   return 0;
106: }

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

113: #endif /*!PETSC_HAVE_LIBPNG*/

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

120:   #include <gif_lib.h>

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

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


153:   GifCMap = GifMakeMapObject(ColorCount, (GifColorType *)palette);
154:   if (!GifCMap) SETERRGIF("Allocating colormap");
155:   GifFile = EGifOpenFileName(filename, 0, NULL);
156:   if (!GifFile) SETERRGIF("Opening");
157:   "Writing screen descriptor", EGifPutScreenDesc(GifFile, Width, Height, ColorRes, 0, GifCMap);
158:   "Writing image descriptor", EGifPutImageDesc(GifFile, 0, 0, Width, Height, 0, NULL);
159:   for (Row = 0; Row < Height; Row++) "Writing image pixels", EGifPutLine(GifFile, (GifPixelType *)pixels + Row * Width, Width);
160:   "Closing", EGifCloseFile(GifFile, NULL);
161:   GifFreeMapObject(GifCMap);
162:   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: {
171:   return PetscDrawImageSaveGIF(filename, palette, w, h, pixels);
172: }

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:   #define SETERRGIF(msg, fn) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, msg " GIF file %s", fn)

184:   if (count < 1) return 0;

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

212:   #undef SETERRGIF
213:   return 0;
214: }

216: #endif /*!PETSC_HAVE_GIFLIB*/

218: /*
219:    Code to write images in JPEG format
220: */
221: #if defined(PETSC_HAVE_LIBJPEG)

223:   #include <jpeglib.h>

225:   #if defined(PETSC_HAVE_SETJMP_H)
226:     #include <setjmp.h>
227: static jmp_buf petsc_jpeg_jumpbuf;
228: static void    petsc_jpeg_error_longjmp(j_common_ptr cinfo)
229: {
230:   (void)cinfo;
231:   longjmp(petsc_jpeg_jumpbuf, 1);
232: }
233:   #endif

235: PETSC_EXTERN PetscErrorCode PetscDrawImageSaveJPG(const char filename[], unsigned char palette[][3], unsigned int w, unsigned int h, const unsigned char pixels[])
236: {
237:   unsigned char              *rgbpixels;
238:   FILE                       *fp;
239:   struct jpeg_compress_struct cinfo;
240:   struct jpeg_error_mgr       jerr;

245:   /* map pixels to RGB colors */
246:   if (palette) {
247:     int                  k, p, n = (int)(w * h);
248:     const unsigned char *colordef;
249:     PetscMalloc1(3 * w * h, &rgbpixels);
250:     for (k = p = 0; k < n; k++) {
251:       colordef       = palette[pixels[k]];
252:       rgbpixels[p++] = colordef[0];
253:       rgbpixels[p++] = colordef[1];
254:       rgbpixels[p++] = colordef[2];
255:     }
256:   } else { /* assume pixels are RGB colors */
257:     rgbpixels = (unsigned char *)pixels;
258:   }
259:   PetscFOpen(PETSC_COMM_SELF, filename, "wb", &fp);

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

287:   PetscFClose(PETSC_COMM_SELF, fp);
288:   if (palette) PetscFree(rgbpixels);
289:   return 0;
290: }

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

297: #endif /*!PETSC_HAVE_LIBJPEG*/

299: static struct {
300:   const char *extension;
301:   PetscErrorCode (*SaveImage)(const char[], unsigned char[][3], unsigned int, unsigned int, const unsigned char[]);
302: } PetscDrawImageSaveTable[] = {
303: #if defined(PETSC_HAVE_LIBPNG)
304:   {".png", PetscDrawImageSave_PNG},
305: #endif
306: #if defined(PETSC_HAVE_GIFLIB)
307:   {".gif", PetscDrawImageSave_GIF},
308: #endif
309: #if defined(PETSC_HAVE_LIBJPEG)
310:   {".jpg", PetscDrawImageSave_JPG},
311: #endif
312:   {".ppm", PetscDrawImageSave_PPM}
313: };

315: PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
316: {
317:   size_t    k;
318:   PetscBool match = PETSC_FALSE;

320:   /* if extension is empty, return default format to caller */
322:   if (!*ext || !**ext) {
323:     *ext = PetscDrawImageSaveTable[0].extension;
324:     return 0;
325:   }
326:   /* check the extension matches a supported format */
328:   for (k = 0; k < PETSC_STATIC_ARRAY_LENGTH(PetscDrawImageSaveTable); k++) {
329:     PetscStrcasecmp(*ext, PetscDrawImageSaveTable[k].extension, &match);
330:     if (match && PetscDrawImageSaveTable[k].SaveImage) return 0;
331:   }
332:   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);
333: }

335: PetscErrorCode PetscDrawImageSave(const char basename[], const char ext[], unsigned char palette[][3], unsigned int w, unsigned int h, const unsigned char pixels[])
336: {
337:   size_t    k;
338:   PetscBool match = PETSC_FALSE;
339:   char      filename[PETSC_MAX_PATH_LEN];


346:   PetscDrawImageCheckFormat(&ext);
347:   PetscSNPrintf(filename, sizeof(filename), "%s%s", basename, ext);
348:   for (k = 0; k < PETSC_STATIC_ARRAY_LENGTH(PetscDrawImageSaveTable); k++) {
349:     PetscStrcasecmp(ext, PetscDrawImageSaveTable[k].extension, &match);
350:     if (match && PetscDrawImageSaveTable[k].SaveImage) {
351:       PetscDrawImageSaveTable[k].SaveImage(filename, palette, w, h, pixels);
352:       return 0;
353:     }
354:   }
355:   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Image extension %s not supported, use .ppm", ext);
356: }

358: PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
359: {
361:   if (!*ext || !**ext) *ext = ".m4v";
362:   return 0;
363: }

365: PetscErrorCode PetscDrawMovieSave(const char basename[], PetscInt count, const char imext[], PetscInt fps, const char mvext[])
366: {
367:   char      input[PETSC_MAX_PATH_LEN];
368:   char      output[PETSC_MAX_PATH_LEN];
369:   PetscBool gifinput;

374:   if (count < 1) return 0;

376:   PetscStrcasecmp(imext, ".gif", &gifinput);
377:   PetscDrawMovieCheckFormat(&mvext);
378:   PetscSNPrintf(input, sizeof(input), "%s/%s_%%d%s", basename, basename, imext);
379:   PetscSNPrintf(output, sizeof(output), "%s%s", basename, mvext);

381:   /* use GIFLIB to generate an intermediate GIF animation */
382: #if defined(PETSC_HAVE_GIFLIB)
383:   if (gifinput) {
384:     char gifmovie[PETSC_MAX_PATH_LEN];
385:     PetscSNPrintf(gifmovie, sizeof(gifmovie), "%s/%s_movie.gif", basename, basename);
386:     PetscDrawMovieSaveGIF(input, count, gifmovie);
387:     PetscStrcpy(input, gifmovie);
388:   }
389: #endif

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