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 */
PetscDrawImageSavePPM(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 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 }
47
PetscDrawImageSave_PPM(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 #if !defined(png_jmpbuf)
62 #define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
63 #endif
64 #endif
65
PetscDrawImageSavePNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 PetscAssertPointer(filename, 1);
75 if (palette) PetscAssertPointer(palette, 2);
76 PetscAssertPointer(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
PetscDrawImageSave_PNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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
PetscDrawImageSaveGIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 PetscAssertPointer(filename, 1);
153 PetscAssertPointer(palette, 2);
154 PetscAssertPointer(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
PetscDrawImageSave_GIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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
PetscDrawMovieSaveGIF(const char pattern[],PetscInt count,const char movie[])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 PetscAssertPointer(pattern, 1);
187 PetscAssertPointer(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;
petsc_jpeg_error_longjmp(j_common_ptr cinfo)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
PetscDrawImageSaveJPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 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));
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
PetscDrawImageSave_JPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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
PetscDrawImageCheckFormat(const char * ext[])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 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 }
340
PetscDrawImageSave(const char basename[],const char ext[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])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 PetscAssertPointer(basename, 1);
349 if (ext) PetscAssertPointer(ext, 2);
350 if (palette) PetscAssertPointer(palette, 3);
351 PetscAssertPointer(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
PetscDrawMovieCheckFormat(const char * ext[])365 PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
366 {
367 PetscFunctionBegin;
368 PetscAssertPointer(ext, 1);
369 if (!*ext || !**ext) *ext = ".m4v";
370 PetscFunctionReturn(PETSC_SUCCESS);
371 }
372
PetscDrawMovieSave(const char basename[],PetscInt count,const char imext[],PetscInt fps,const char mvext[])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 PetscAssertPointer(basename, 1);
381 PetscAssertPointer(imext, 3);
382 if (mvext) PetscAssertPointer(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 %" PetscInt_FMT, fps));
407 if (gifinput) {
408 PetscCall(PetscStrlcat(options, " -f gif", sizeof(options)));
409 PetscCall(PetscSNPrintf(extraopts, sizeof(extraopts), " -default_delay %" PetscInt_FMT, (fps > 0) ? 100 / fps : 4));
410 } else {
411 PetscCall(PetscStrlcat(options, " -f image2", sizeof(options)));
412 if (fps > 0) PetscCall(PetscSNPrintf(extraopts, sizeof(extraopts), " -framerate %" PetscInt_FMT, 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