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