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;

 18:   PetscFunctionBegin;
 19:   PetscAssertPointer(filename, 1);
 20:   if (palette) PetscAssertPointer(palette, 2);
 21:   PetscAssertPointer(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(PETSC_SUCCESS);
 46: }

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

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

 58:   #include <png.h>

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

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

 73:   PetscFunctionBegin;
 74:   PetscAssertPointer(filename, 1);
 75:   if (palette) PetscAssertPointer(palette, 2);
 76:   PetscAssertPointer(pixels, 5);

 78:   /* open file and create libpng structures */
 79:   PetscCall(PetscFOpen(PETSC_COMM_SELF, filename, "wb", &fp));
 80:   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 81:   PetscCheck(png_ptr, PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot create PNG context");
 82:   info_ptr = png_create_info_struct(png_ptr);
 83:   PetscCheck(info_ptr, PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot create PNG context");

 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:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error writing PNG file %s", filename);
 91:   }
 92:   #endif

 94:   /* setup PNG image metadata */
 95:   png_init_io(png_ptr, fp);
 96:   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);
 97:   if (palette) png_set_PLTE(png_ptr, info_ptr, (png_color *)palette, 256);

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

104:   /* destroy libpng structures and close file */
105:   png_destroy_write_struct(&png_ptr, &info_ptr);
106:   PetscCall(PetscFClose(PETSC_COMM_SELF, fp));
107:   PetscFunctionReturn(PETSC_SUCCESS);
108: }

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

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, ...) \
146:     do { \
147:       int Error = __VA_ARGS__; \
148:       if (PetscUnlikely(Error != GIF_OK)) SETERRGIF(msg); \
149:     } while (0)

151:   PetscFunctionBegin;
152:   PetscAssertPointer(filename, 1);
153:   PetscAssertPointer(palette, 2);
154:   PetscAssertPointer(pixels, 5);

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

167:   #undef SETERRGIF
168:   #undef CHKERRGIF
169:   PetscFunctionReturn(PETSC_SUCCESS);
170: }

172: static PetscErrorCode PetscDrawImageSave_GIF(const char filename[], unsigned char palette[][3], unsigned int w, unsigned int h, const unsigned char pixels[])
173: {
174:   return PetscDrawImageSaveGIF(filename, palette, w, h, pixels);
175: }

177: PETSC_EXTERN PetscErrorCode PetscDrawMovieSaveGIF(const char pattern[], PetscInt count, const char movie[])
178: {
179:   int          i, j, Row;
180:   char         image[PETSC_MAX_PATH_LEN];
181:   GifFileType *GifMovie = NULL;
182:   GifFileType *GifImage = NULL;
183:   #define SETERRGIF(msg, fn) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, msg " GIF file %s", fn)

185:   PetscFunctionBegin;
186:   PetscAssertPointer(pattern, 1);
187:   PetscAssertPointer(movie, 3);
188:   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);

190:   for (i = 0; i < count; i++) {
191:     PetscCall(PetscSNPrintf(image, sizeof(image), pattern, (int)i));
192:     /* open and read image file */
193:     if ((GifImage = DGifOpenFileName(image, NULL)) == NULL) SETERRGIF("Opening input", image);
194:     if (DGifSlurp(GifImage) != GIF_OK) SETERRGIF("Reading input", image);
195:     /* open movie file and write header */
196:     if (i == 0) {
197:       if ((GifMovie = EGifOpenFileName(movie, 0, NULL)) == NULL) SETERRGIF("Opening output", movie);
198:       if (EGifPutScreenDesc(GifMovie, GifImage->SWidth, GifImage->SHeight, GifImage->SColorResolution, GifImage->SBackGroundColor, 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 && !memcmp(GifMovie->SColorMap->Colors, FrameColorMap->Colors, (size_t)FrameColorMap->ColorCount * sizeof(GifColorType))) FrameColorMap = NULL;
206:       /* add frame to movie */
207:       if (EGifPutImageDesc(GifMovie, GifFrame->Left, GifFrame->Top, GifFrame->Width, GifFrame->Height, GifFrame->Interlace, FrameColorMap) != GIF_OK) SETERRGIF("Writing image descriptor,", movie);
208:       for (Row = 0; Row < GifFrame->Height; Row++) {
209:         if (EGifPutLine(GifMovie, sp->RasterBits + Row * GifFrame->Width, GifFrame->Width) != GIF_OK) SETERRGIF("Writing image pixels,", movie);
210:       }
211:     }
212:     if (DGifCloseFile(GifImage, NULL) != GIF_OK) SETERRGIF("Closing input", image);
213:   }
214:   if (EGifCloseFile(GifMovie, NULL) != GIF_OK) SETERRGIF("Closing output", movie);

216:   #undef SETERRGIF
217:   PetscFunctionReturn(PETSC_SUCCESS);
218: }

220: #endif /*!PETSC_HAVE_GIFLIB*/

222: /*
223:    Code to write images in JPEG format
224: */
225: #if defined(PETSC_HAVE_LIBJPEG)

227:   #include <jpeglib.h>

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

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

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

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

292:   PetscCall(PetscFClose(PETSC_COMM_SELF, fp));
293:   if (palette) PetscCall(PetscFree(rgbpixels));
294:   PetscFunctionReturn(PETSC_SUCCESS);
295: }

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

302: #endif /*!PETSC_HAVE_LIBJPEG*/

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

320: PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
321: {
322:   size_t    k;
323:   PetscBool match = PETSC_FALSE;

325:   PetscFunctionBegin;
326:   /* if extension is empty, return default format to caller */
327:   PetscAssertPointer(ext, 1);
328:   if (!*ext || !**ext) {
329:     *ext = PetscDrawImageSaveTable[0].extension;
330:     PetscFunctionReturn(PETSC_SUCCESS);
331:   }
332:   /* check the extension matches a supported format */
333:   PetscAssertPointer(*ext, 1);
334:   for (k = 0; k < PETSC_STATIC_ARRAY_LENGTH(PetscDrawImageSaveTable); k++) {
335:     PetscCall(PetscStrcasecmp(*ext, PetscDrawImageSaveTable[k].extension, &match));
336:     if (match && PetscDrawImageSaveTable[k].SaveImage) PetscFunctionReturn(PETSC_SUCCESS);
337:   }
338:   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);
339: }

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

347:   PetscFunctionBegin;
348:   PetscAssertPointer(basename, 1);
349:   if (ext) PetscAssertPointer(ext, 2);
350:   if (palette) PetscAssertPointer(palette, 3);
351:   PetscAssertPointer(pixels, 6);

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

365: PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
366: {
367:   PetscFunctionBegin;
368:   PetscAssertPointer(ext, 1);
369:   if (!*ext || !**ext) *ext = ".m4v";
370:   PetscFunctionReturn(PETSC_SUCCESS);
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;

379:   PetscFunctionBegin;
380:   PetscAssertPointer(basename, 1);
381:   PetscAssertPointer(imext, 3);
382:   if (mvext) PetscAssertPointer(mvext, 5);
383:   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);

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

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

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