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