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: }