xref: /petsc/src/sys/classes/draw/utils/image.c (revision b122ec5aa1bd4469eb4e0673542fb7de3f411254)
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) PetscValidCharPointer(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     CHKERRQ(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   CHKERRQ(PetscBinaryOpen(filename,FILE_MODE_WRITE,&fd));
38   CHKERRQ(PetscSNPrintf(header,sizeof(header),"P6\n%d %d\n255\n%c",(int)w,(int)h,'\0'));
39   CHKERRQ(PetscStrlen(header,&hdrlen));
40   CHKERRQ(PetscBinaryWrite(fd,header,hdrlen,PETSC_CHAR));
41   /* write image data and close file */
42   CHKERRQ(PetscBinaryWrite(fd,rgb,3*w*h,PETSC_CHAR));
43   CHKERRQ(PetscBinaryClose(fd));
44   if (palette) CHKERRQ(PetscFree(rgb));
45   PetscFunctionReturn(0);
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 { return PetscDrawImageSavePPM(filename,palette,w,h,pixels); }
50 
51 /*
52    Code to write images in PNG format
53 */
54 #if defined(PETSC_HAVE_LIBPNG)
55 
56 #include <png.h>
57 
58 #if defined(PNG_SETJMP_SUPPORTED)
59 # ifndef png_jmpbuf
60 #   define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
61 # endif
62 #endif
63 
64 PETSC_EXTERN PetscErrorCode PetscDrawImageSavePNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
65 {
66   FILE           *fp;
67   png_struct     *png_ptr;
68   png_info       *info_ptr;
69   unsigned int   row, stride = palette ? w : 3*w;
70 
71   PetscFunctionBegin;
72   PetscValidCharPointer(filename,1);
73   if (palette) PetscValidCharPointer(palette,2);
74   PetscValidCharPointer(pixels,5);
75 
76   /* open file and create libpng structures */
77   CHKERRQ(PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp));
78   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
79   PetscCheck(png_ptr,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot create PNG context");
80   info_ptr = png_create_info_struct(png_ptr);
81   PetscCheck(info_ptr,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot create PNG context");
82 
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
91 
92   /* setup PNG image metadata */
93   png_init_io(png_ptr, fp);
94   png_set_IHDR(png_ptr, info_ptr, w, h, /*depth*/8,
95                palette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
96                PNG_INTERLACE_NONE,
97                PNG_COMPRESSION_TYPE_DEFAULT,
98                PNG_FILTER_TYPE_DEFAULT);
99   if (palette)
100     png_set_PLTE(png_ptr, info_ptr, (png_color*)palette, 256);
101 
102   /* write PNG image header and data */
103   png_write_info(png_ptr, info_ptr);
104   for (row = 0; row < h; row++)
105     png_write_row(png_ptr, pixels + row*stride);
106   png_write_end(png_ptr, NULL);
107 
108   /* destroy libpng structures and close file */
109   png_destroy_write_struct(&png_ptr, &info_ptr);
110   CHKERRQ(PetscFClose(PETSC_COMM_SELF,fp));
111   PetscFunctionReturn(0);
112 }
113 
114 static PetscErrorCode PetscDrawImageSave_PNG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
115 { return PetscDrawImageSavePNG(filename,palette,w,h,pixels); }
116 
117 #endif/*!PETSC_HAVE_LIBPNG*/
118 
119 /*
120    Code to write images in GIF format
121 */
122 #if defined(PETSC_HAVE_GIFLIB)
123 
124 #include <gif_lib.h>
125 
126 #if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5
127 #define GifMakeMapObject          MakeMapObject
128 #define GifFreeMapObject          FreeMapObject
129 #define EGifOpenFileName(n,b,err) EGifOpenFileName(n,b)
130 #define EGifOpenFileHandle(h,err) EGifOpenFileName(h)
131 #define EGifCloseFile(f,err)      EGifCloseFile(f)
132 #define DGifOpenFileName(n,err)   DGifOpenFileName(n)
133 #define DGifOpenFileHandle(h,err) DGifOpenFileName(h)
134 #define DGifCloseFile(f,err)      DGifCloseFile(f)
135 #endif
136 
137 PETSC_EXTERN PetscErrorCode PetscDrawImageSaveGIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
138 {
139   int            Row, Error;
140   int            Width  = (int)w;
141   int            Height = (int)h;
142   int            ColorRes   = 8;
143   int            ColorCount = 256;
144   ColorMapObject *GifCMap = NULL;
145   GifFileType    *GifFile = NULL;
146 # define         SETERRGIF(msg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg", GIF file: %s",filename)
147 # define         CHKERRGIF(msg) do {if (Error != GIF_OK) SETERRGIF(msg);} while (0)
148 
149   PetscFunctionBegin;
150   PetscValidCharPointer(filename,1);
151   PetscValidCharPointer(palette,2);
152   PetscValidCharPointer(pixels,5);
153 
154   GifCMap = GifMakeMapObject(ColorCount, (GifColorType*)palette); if (!GifCMap) SETERRGIF("Allocating colormap");
155   GifFile = EGifOpenFileName(filename, 0, NULL); if (!GifFile) SETERRGIF("Opening");
156   Error = EGifPutScreenDesc(GifFile, Width, Height, ColorRes, 0, GifCMap); CHKERRGIF("Writing screen descriptor");
157   Error = EGifPutImageDesc(GifFile, 0, 0, Width, Height, 0, NULL); CHKERRGIF("Writing image descriptor");
158   for (Row = 0; Row < Height; Row++) {
159     Error = EGifPutLine(GifFile, (GifPixelType*)pixels + Row*Width, Width); CHKERRGIF("Writing image pixels");
160   }
161   Error = EGifCloseFile(GifFile, NULL); CHKERRGIF("Closing");
162   GifFreeMapObject(GifCMap); GifCMap = NULL;
163 
164 # undef SETERRGIF
165 # undef CHKERRGIF
166   PetscFunctionReturn(0);
167 }
168 
169 static PetscErrorCode PetscDrawImageSave_GIF(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
170 { return PetscDrawImageSaveGIF(filename,palette,w,h,pixels); }
171 
172 PETSC_EXTERN PetscErrorCode PetscDrawMovieSaveGIF(const char pattern[],PetscInt count,const char movie[])
173 {
174   int            i,j,Row;
175   char           image[PETSC_MAX_PATH_LEN];
176   GifFileType    *GifMovie = NULL;
177   GifFileType    *GifImage = NULL;
178 # define         SETERRGIF(msg,fn) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,msg" GIF file %s",fn)
179 
180   PetscFunctionBegin;
181   PetscValidCharPointer(pattern,1);
182   PetscValidCharPointer(movie,3);
183   if (count < 1) PetscFunctionReturn(0);
184 
185   for (i = 0; i < count; i++) {
186     CHKERRQ(PetscSNPrintf(image,sizeof(image),pattern,(int)i));
187     /* open and read image file */
188     if ((GifImage = DGifOpenFileName(image, NULL)) == NULL) SETERRGIF("Opening input",image);
189     if (DGifSlurp(GifImage) != GIF_OK) SETERRGIF("Reading input",image);
190     /* open movie file and write header */
191     if (i == 0) {
192       if ((GifMovie = EGifOpenFileName(movie, 0, NULL)) == NULL) SETERRGIF("Opening output",movie);
193       if (EGifPutScreenDesc(GifMovie,
194                             GifImage->SWidth,
195                             GifImage->SHeight,
196                             GifImage->SColorResolution,
197                             GifImage->SBackGroundColor,
198                             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 &&
206           !memcmp(GifMovie->SColorMap->Colors,FrameColorMap->Colors,
207                   (size_t)FrameColorMap->ColorCount*sizeof(GifColorType)))
208         FrameColorMap = NULL;
209       /* add frame to movie */
210       if (EGifPutImageDesc(GifMovie,
211                            GifFrame->Left,
212                            GifFrame->Top,
213                            GifFrame->Width,
214                            GifFrame->Height,
215                            GifFrame->Interlace,
216                            FrameColorMap) != GIF_OK) SETERRGIF("Writing image descriptor,",movie);
217       for (Row = 0; Row < GifFrame->Height; Row++) {
218         if (EGifPutLine(GifMovie,
219                         sp->RasterBits + Row * GifFrame->Width,
220                         GifFrame->Width) != GIF_OK) SETERRGIF("Writing image pixels,",movie);
221       }
222     }
223     if (DGifCloseFile(GifImage, NULL) != GIF_OK) SETERRGIF("Closing input",image);
224   }
225   if (EGifCloseFile(GifMovie, NULL) != GIF_OK) SETERRGIF("Closing output",movie);
226 
227 # undef SETERRGIF
228   PetscFunctionReturn(0);
229 }
230 
231 #endif/*!PETSC_HAVE_GIFLIB*/
232 
233 /*
234    Code to write images in JPEG format
235 */
236 #if defined(PETSC_HAVE_LIBJPEG)
237 
238 #include <jpeglib.h>
239 
240 #if defined(PETSC_HAVE_SETJMP_H)
241 #include <setjmp.h>
242 static jmp_buf petsc_jpeg_jumpbuf;
243 static void petsc_jpeg_error_longjmp (j_common_ptr cinfo) { (void)cinfo; longjmp(petsc_jpeg_jumpbuf,1); }
244 #endif
245 
246 PETSC_EXTERN PetscErrorCode PetscDrawImageSaveJPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
247 {
248   unsigned char               *rgbpixels;
249   FILE                        *fp;
250   struct jpeg_compress_struct cinfo;
251   struct jpeg_error_mgr       jerr;
252 
253   PetscFunctionBegin;
254   PetscValidCharPointer(filename,1);
255   if (palette) PetscValidCharPointer(palette,2);
256   PetscValidCharPointer(pixels,5);
257   /* map pixels to RGB colors */
258   if (palette) {
259     int k,p,n = (int)(w*h);
260     const unsigned char *colordef;
261     CHKERRQ(PetscMalloc1(3*w*h,&rgbpixels));
262     for (k=p=0; k<n; k++) {
263       colordef = palette[pixels[k]];
264       rgbpixels[p++] = colordef[0];
265       rgbpixels[p++] = colordef[1];
266       rgbpixels[p++] = colordef[2];
267     }
268   } else { /* assume pixels are RGB colors */
269     rgbpixels = (unsigned char*)pixels;
270   }
271   CHKERRQ(PetscFOpen(PETSC_COMM_SELF,filename,"wb",&fp));
272 
273   cinfo.err = jpeg_std_error(&jerr);
274 #if defined(PETSC_HAVE_SETJMP_H)
275   jerr.error_exit = petsc_jpeg_error_longjmp;
276   if (setjmp(petsc_jpeg_jumpbuf)) {
277     char message[JMSG_LENGTH_MAX];
278     jerr.format_message((j_common_ptr)&cinfo,message);
279     jpeg_destroy_compress(&cinfo);
280     (void)PetscFClose(PETSC_COMM_SELF,fp);
281     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Error writing JPEG file %s\n%s",filename,message);
282   }
283 #endif
284   jpeg_create_compress(&cinfo);
285   jpeg_stdio_dest(&cinfo,fp);
286   cinfo.image_width      = w;
287   cinfo.image_height     = h;
288   cinfo.input_components = 3;
289   cinfo.in_color_space   = JCS_RGB;
290   jpeg_set_defaults(&cinfo);
291   jpeg_start_compress(&cinfo,TRUE);
292   while (cinfo.next_scanline < cinfo.image_height) {
293     unsigned char *rowptr = rgbpixels + cinfo.next_scanline * 3*w;
294     (void)jpeg_write_scanlines(&cinfo,&rowptr,1);
295   }
296   jpeg_finish_compress(&cinfo);
297   jpeg_destroy_compress(&cinfo);
298 
299   CHKERRQ(PetscFClose(PETSC_COMM_SELF,fp));
300   if (palette) CHKERRQ(PetscFree(rgbpixels));
301   PetscFunctionReturn(0);
302 }
303 
304 static PetscErrorCode PetscDrawImageSave_JPG(const char filename[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
305 { return PetscDrawImageSaveJPG(filename,palette,w,h,pixels); }
306 
307 #endif/*!PETSC_HAVE_LIBJPEG*/
308 
309 static struct {
310   const char      *extension;
311   PetscErrorCode (*SaveImage)(const char[],unsigned char[][3],unsigned int,unsigned int,const unsigned char[]);
312 } PetscDrawImageSaveTable[] = {
313 #if defined(PETSC_HAVE_LIBPNG)
314   {".png", PetscDrawImageSave_PNG},
315 #endif
316 #if defined(PETSC_HAVE_GIFLIB)
317   {".gif", PetscDrawImageSave_GIF},
318 #endif
319 #if defined(PETSC_HAVE_LIBJPEG)
320   {".jpg", PetscDrawImageSave_JPG},
321 #endif
322   {".ppm", PetscDrawImageSave_PPM}
323 };
324 
325 PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
326 {
327   size_t         k;
328   PetscBool      match = PETSC_FALSE;
329 
330   PetscFunctionBegin;
331   /* if extension is empty, return default format to caller */
332   PetscValidPointer(ext,1);
333   if (!*ext || !**ext) {
334     *ext = PetscDrawImageSaveTable[0].extension;
335     PetscFunctionReturn(0);
336   }
337   /* check the extension matches a supported format */
338   PetscValidCharPointer(*ext,1);
339   for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) {
340     CHKERRQ(PetscStrcasecmp(*ext,PetscDrawImageSaveTable[k].extension,&match));
341     if (match && PetscDrawImageSaveTable[k].SaveImage) PetscFunctionReturn(0);
342   }
343   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);
344 }
345 
346 PetscErrorCode PetscDrawImageSave(const char basename[],const char ext[],unsigned char palette[][3],unsigned int w,unsigned int h,const unsigned char pixels[])
347 {
348   size_t         k;
349   PetscBool      match = PETSC_FALSE;
350   char           filename[PETSC_MAX_PATH_LEN];
351 
352   PetscFunctionBegin;
353   PetscValidCharPointer(basename,1);
354   if (ext) PetscValidCharPointer(ext,2);
355   if (palette) PetscValidPointer(palette,3);
356   PetscValidCharPointer(pixels,6);
357 
358   CHKERRQ(PetscDrawImageCheckFormat(&ext));
359   CHKERRQ(PetscSNPrintf(filename,sizeof(filename),"%s%s",basename,ext));
360   for (k=0; k<sizeof(PetscDrawImageSaveTable)/sizeof(PetscDrawImageSaveTable[0]); k++) {
361     CHKERRQ(PetscStrcasecmp(ext,PetscDrawImageSaveTable[k].extension,&match));
362     if (match && PetscDrawImageSaveTable[k].SaveImage) {
363       CHKERRQ(PetscDrawImageSaveTable[k].SaveImage(filename,palette,w,h,pixels));
364       PetscFunctionReturn(0);
365     }
366   }
367   SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Image extension %s not supported, use .ppm",ext);
368 }
369 
370 PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
371 {
372   PetscFunctionBegin;
373   PetscValidPointer(ext,1);
374   if (!*ext || !**ext) *ext = ".m4v";
375   PetscFunctionReturn(0);
376 }
377 
378 PetscErrorCode PetscDrawMovieSave(const char basename[],PetscInt count,const char imext[],PetscInt fps,const char mvext[])
379 {
380   char           input[PETSC_MAX_PATH_LEN];
381   char           output[PETSC_MAX_PATH_LEN];
382   PetscBool      gifinput;
383 
384   PetscFunctionBegin;
385   PetscValidCharPointer(basename,1);
386   PetscValidCharPointer(imext,3);
387   if (mvext) PetscValidCharPointer(mvext,5);
388   if (count < 1) PetscFunctionReturn(0);
389 
390   CHKERRQ(PetscStrcasecmp(imext,".gif",&gifinput));
391   CHKERRQ(PetscDrawMovieCheckFormat(&mvext));
392   CHKERRQ(PetscSNPrintf(input,sizeof(input),"%s/%s_%%d%s",basename,basename,imext));
393   CHKERRQ(PetscSNPrintf(output,sizeof(output),"%s%s",basename,mvext));
394 
395   /* use GIFLIB to generate an intermediate GIF animation */
396 #if defined(PETSC_HAVE_GIFLIB)
397   if (gifinput) {
398     char gifmovie[PETSC_MAX_PATH_LEN];
399     CHKERRQ(PetscSNPrintf(gifmovie,sizeof(gifmovie),"%s/%s_movie.gif",basename,basename));
400     CHKERRQ(PetscDrawMovieSaveGIF(input,count,gifmovie));
401     CHKERRQ(PetscStrcpy(input,gifmovie));
402   }
403 #endif
404 
405   /* use FFmpeg to generate a movie */
406 #if defined(PETSC_HAVE_POPEN)
407   {
408     FILE *fd;
409     char options[64] = "-loglevel error -y", extraopts[32] = "", framerate[24] = "";
410     char command[sizeof(options)+sizeof(extraopts)+sizeof(framerate)+PETSC_MAX_PATH_LEN*2];
411     if (fps > 0) CHKERRQ(PetscSNPrintf(framerate,sizeof(framerate),"-r %d",(int)fps));
412     if (gifinput) {
413       CHKERRQ(PetscStrlcat(options," -f gif",sizeof(options)));
414       CHKERRQ(PetscSNPrintf(extraopts,sizeof(extraopts)," -default_delay %d",(fps > 0) ? 100/(int)fps : 4));
415     } else {
416       CHKERRQ(PetscStrlcat(options," -f image2",sizeof(options)));
417       if (fps > 0) CHKERRQ(PetscSNPrintf(extraopts,sizeof(extraopts)," -framerate %d",(int)fps));
418     }
419     if (extraopts[0]) CHKERRQ(PetscStrlcat(options,extraopts,sizeof(options)));
420     CHKERRQ(PetscSNPrintf(command,sizeof(command),"ffmpeg %s -i \"%s\" %s \"%s\"",options,input,framerate,output));
421     CHKERRQ(PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd));
422     CHKERRQ(PetscPClose(PETSC_COMM_SELF,fd));
423   }
424 #endif
425   PetscFunctionReturn(0);
426 }
427