xref: /petsc/src/sys/classes/draw/utils/image.c (revision 0226ec35a99031c5bdd82a492055a25793359ffb)
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) PetscValidPointer(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     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 }
47 
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 }
52 
53 /*
54    Code to write images in PNG format
55 */
56 #if defined(PETSC_HAVE_LIBPNG)
57 
58   #include <png.h>
59 
60   #if defined(PNG_SETJMP_SUPPORTED)
61     #ifndef png_jmpbuf
62       #define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
63     #endif
64   #endif
65 
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;
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   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");
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     SETERRQ(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, 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);
98 
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);
103 
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 }
109 
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 }
114 
115 #endif /*!PETSC_HAVE_LIBPNG*/
116 
117 /*
118    Code to write images in GIF format
119 */
120 #if defined(PETSC_HAVE_GIFLIB)
121 
122   #include <gif_lib.h>
123 
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
134 
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)
150 
151   PetscFunctionBegin;
152   PetscValidCharPointer(filename, 1);
153   PetscValidCharPointer(palette, 2);
154   PetscValidCharPointer(pixels, 5);
155 
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;
166 
167   #undef SETERRGIF
168   #undef CHKERRGIF
169   PetscFunctionReturn(PETSC_SUCCESS);
170 }
171 
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 }
176 
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)
184 
185   PetscFunctionBegin;
186   PetscValidCharPointer(pattern, 1);
187   PetscValidCharPointer(movie, 3);
188   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);
189 
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);
215 
216   #undef SETERRGIF
217   PetscFunctionReturn(PETSC_SUCCESS);
218 }
219 
220 #endif /*!PETSC_HAVE_GIFLIB*/
221 
222 /*
223    Code to write images in JPEG format
224 */
225 #if defined(PETSC_HAVE_LIBJPEG)
226 
227   #include <jpeglib.h>
228 
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
238 
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;
245 
246   PetscFunctionBegin;
247   PetscValidCharPointer(filename, 1);
248   if (palette) PetscValidCharPointer(palette, 2);
249   PetscValidCharPointer(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));
265 
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);
291 
292   PetscCall(PetscFClose(PETSC_COMM_SELF, fp));
293   if (palette) PetscCall(PetscFree(rgbpixels));
294   PetscFunctionReturn(PETSC_SUCCESS);
295 }
296 
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 }
301 
302 #endif /*!PETSC_HAVE_LIBJPEG*/
303 
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 };
319 
320 PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
321 {
322   size_t    k;
323   PetscBool match = PETSC_FALSE;
324 
325   PetscFunctionBegin;
326   /* if extension is empty, return default format to caller */
327   PetscValidPointer(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   PetscValidCharPointer(*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 }
340 
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];
346 
347   PetscFunctionBegin;
348   PetscValidCharPointer(basename, 1);
349   if (ext) PetscValidCharPointer(ext, 2);
350   if (palette) PetscValidPointer(palette, 3);
351   PetscValidCharPointer(pixels, 6);
352 
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 }
364 
365 PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
366 {
367   PetscFunctionBegin;
368   PetscValidPointer(ext, 1);
369   if (!*ext || !**ext) *ext = ".m4v";
370   PetscFunctionReturn(PETSC_SUCCESS);
371 }
372 
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;
378 
379   PetscFunctionBegin;
380   PetscValidCharPointer(basename, 1);
381   PetscValidCharPointer(imext, 3);
382   if (mvext) PetscValidCharPointer(mvext, 5);
383   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);
384 
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));
389 
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
399 
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 }
422