xref: /petsc/src/sys/classes/draw/interface/dsave.c (revision d71ae5a4db6382e7f06317b8d368875286fe9008)
1 #include <petsc/private/drawimpl.h> /*I "petscdraw.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 #if defined(PETSC_HAVE_SAWS)
9 static PetscErrorCode PetscDrawSave_SAWs(PetscDraw);
10 #endif
11 
12 /*@C
13    PetscDrawSetSave - Saves images produced in a `PetscDraw` into a file
14 
15    Collective on draw
16 
17    Input Parameters:
18 +  draw      - the graphics context
19 -  filename  - name of the file, if .ext then uses name of draw object plus .ext using .ext to determine the image type
20 
21    Options Database Command:
22 +  -draw_save <filename>  - filename could be name.ext or .ext (where .ext determines the type of graphics file to save, for example .png)
23 .  -draw_save_final_image [optional filename] - saves the final image displayed in a window
24 -  -draw_save_single_file - saves each new image in the same file, normally each new image is saved in a new file with filename/filename_%d.ext
25 
26    Level: intermediate
27 
28    Note:
29     You should call this BEFORE creating your image and calling `PetscDrawSave()`.
30    The supported image types are .png, .gif, .jpg, and .ppm (PETSc chooses the default in that order).
31    Support for .png images requires configure --with-libpng.
32    Support for .gif images requires configure --with-giflib.
33    Support for .jpg images requires configure --with-libjpeg.
34    Support for .ppm images is built-in. The PPM format has no compression (640x480 pixels ~ 900 KiB).
35 
36 .seealso: `PetscDraw`, `PetscDrawOpenX()`, `PetscDrawOpenImage()`, `PetscDrawSetFromOptions()`, `PetscDrawCreate()`, `PetscDrawDestroy()`, `PetscDrawSetSaveFinalImage()`
37 @*/
38 PetscErrorCode PetscDrawSetSave(PetscDraw draw, const char filename[])
39 {
40   const char *savename = NULL;
41   const char *imageext = NULL;
42   char        buf[PETSC_MAX_PATH_LEN];
43 
44   PetscFunctionBegin;
45   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
46   if (filename) PetscValidCharPointer(filename, 2);
47 
48   /* determine save filename and image extension */
49   if (filename && filename[0]) {
50     PetscCall(PetscStrchr(filename, '.', (char **)&imageext));
51     if (!imageext) savename = filename;
52     else if (imageext != filename) {
53       size_t l1 = 0, l2 = 0;
54       PetscCall(PetscStrlen(filename, &l1));
55       PetscCall(PetscStrlen(imageext, &l2));
56       PetscCall(PetscStrncpy(buf, filename, l1 - l2 + 1));
57       savename = buf;
58     }
59   }
60 
61   if (!savename) PetscCall(PetscObjectGetName((PetscObject)draw, &savename));
62   PetscCall(PetscDrawImageCheckFormat(&imageext));
63 
64   draw->savefilecount = 0;
65   PetscCall(PetscFree(draw->savefilename));
66   PetscCall(PetscFree(draw->saveimageext));
67   PetscCall(PetscStrallocpy(savename, &draw->savefilename));
68   PetscCall(PetscStrallocpy(imageext, &draw->saveimageext));
69 
70   if (draw->savesinglefile) {
71     PetscCall(PetscInfo(NULL, "Will save image to file %s%s\n", draw->savefilename, draw->saveimageext));
72   } else {
73     PetscCall(PetscInfo(NULL, "Will save images to file %s/%s_%%d%s\n", draw->savefilename, draw->savefilename, draw->saveimageext));
74   }
75   PetscFunctionReturn(0);
76 }
77 
78 /*@C
79    PetscDrawSetSaveMovie - Saves a movie produced from a `PetscDraw` into a file
80 
81    Collective on draw
82 
83    Input Parameters:
84 +  draw      - the graphics context
85 -  movieext  - optional extension defining the movie format
86 
87    Options Database Command:
88 .  -draw_save_movie <.ext> - saves a movie with extension .ext
89 
90    Level: intermediate
91 
92    Note:
93     You should call this AFTER calling `PetscDrawSetSave()` and BEFORE creating your image with `PetscDrawSave()`.
94    The ffmpeg utility must be in your path to make the movie.
95 
96 .seealso: `PetscDraw`, `PetscDrawSetSave()`, `PetscDrawSetFromOptions()`, `PetscDrawCreate()`, `PetscDrawDestroy()`
97 @*/
98 PetscErrorCode PetscDrawSetSaveMovie(PetscDraw draw, const char movieext[])
99 {
100   PetscFunctionBegin;
101   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
102   if (movieext) PetscValidCharPointer(movieext, 2);
103 
104   if (!draw->savefilename) PetscCall(PetscDrawSetSave(draw, ""));
105   PetscCall(PetscDrawMovieCheckFormat(&movieext));
106   PetscCall(PetscStrallocpy(movieext, &draw->savemovieext));
107   draw->savesinglefile = PETSC_FALSE; /* otherwise we cannot generage movies */
108 
109   PetscCall(PetscInfo(NULL, "Will save movie to file %s%s\n", draw->savefilename, draw->savemovieext));
110   PetscFunctionReturn(0);
111 }
112 
113 /*@C
114    PetscDrawSetSaveFinalImage - Saves the final image produced in a `PetscDraw` into a file
115 
116    Collective on draw
117 
118    Input Parameters:
119 +  draw      - the graphics context
120 -  filename  - name of the file, if NULL or empty uses name set with `PetscDrawSetSave()` or the name of the draw object
121 
122    Options Database Command:
123 .  -draw_save_final_image  <filename> - filename could be name.ext or .ext (where .ext determines the type of graphics file to save, for example .png)
124 
125    Level: intermediate
126 
127    Notes:
128     You should call this BEFORE creating your image and calling `PetscDrawSave()`.
129 
130    The supported image types are .png, .gif, and .ppm (PETSc chooses the default in that order).
131 .vb
132    Support for .png images requires configure --with-libpng.
133    Support for .gif images requires configure --with-giflib.
134    Support for .jpg images requires configure --with-libjpeg.
135    Support for .ppm images is built-in. The PPM format has no compression (640x480 pixels ~ 900 KiB).
136 .ve
137 
138 .seealso: `PetscDraw`, `PetscDrawSetSave()`, `PetscDrawSetFromOptions()`, `PetscDrawCreate()`, `PetscDrawDestroy()`
139 @*/
140 PetscErrorCode PetscDrawSetSaveFinalImage(PetscDraw draw, const char filename[])
141 {
142   char buf[PETSC_MAX_PATH_LEN];
143 
144   PetscFunctionBegin;
145   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
146   if (!filename || !filename[0]) {
147     if (!draw->savefilename) {
148       PetscCall(PetscObjectGetName((PetscObject)draw, &filename));
149     } else {
150       PetscCall(PetscSNPrintf(buf, sizeof(buf), "%s%s", draw->savefilename, draw->saveimageext));
151       filename = buf;
152     }
153   }
154   PetscCall(PetscFree(draw->savefinalfilename));
155   PetscCall(PetscStrallocpy(filename, &draw->savefinalfilename));
156   PetscFunctionReturn(0);
157 }
158 
159 /*@
160    PetscDrawSave - Saves a drawn image
161 
162    Collective on draw
163 
164    Input Parameters:
165 .  draw - the drawing context
166 
167    Level: advanced
168 
169    Note:
170     this is not normally called by the user.
171 
172 .seealso: `PetscDraw`, `PetscDrawSetSave()`
173 @*/
174 PetscErrorCode PetscDrawSave(PetscDraw draw)
175 {
176   PetscInt       saveindex;
177   char           basename[PETSC_MAX_PATH_LEN];
178   unsigned char  palette[256][3];
179   unsigned int   w, h;
180   unsigned char *pixels = NULL;
181   PetscMPIInt    rank;
182 
183   PetscFunctionBegin;
184   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
185   if (!draw->ops->save && !draw->ops->getimage) PetscFunctionReturn(0);
186   if (draw->ops->save) {
187     PetscUseTypeMethod(draw, save);
188     goto finally;
189   }
190   if (!draw->savefilename || !draw->saveimageext) PetscFunctionReturn(0);
191   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)draw), &rank));
192 
193   saveindex = draw->savefilecount++;
194 
195   if (rank == 0 && !saveindex) {
196     char path[PETSC_MAX_PATH_LEN];
197     if (draw->savesinglefile) {
198       PetscCall(PetscSNPrintf(path, sizeof(path), "%s%s", draw->savefilename, draw->saveimageext));
199       (void)remove(path);
200     } else {
201       PetscCall(PetscSNPrintf(path, sizeof(path), "%s", draw->savefilename));
202       PetscCall(PetscRMTree(path));
203       PetscCall(PetscMkdir(path));
204     }
205     if (draw->savemovieext) {
206       PetscCall(PetscSNPrintf(path, sizeof(path), "%s%s", draw->savefilename, draw->savemovieext));
207       (void)remove(path);
208     }
209   }
210   if (draw->savesinglefile) {
211     PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s", draw->savefilename));
212   } else {
213     char *basefilename;
214 
215     PetscCall(PetscStrrchr(draw->savefilename, '/', (char **)&basefilename));
216     if (basefilename != draw->savefilename) {
217       PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s_%d", draw->savefilename, (int)saveindex));
218     } else {
219       PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s/%s_%d", draw->savefilename, draw->savefilename, (int)saveindex));
220     }
221   }
222 
223   /* this call is collective, only the first process gets the image data */
224   PetscUseTypeMethod(draw, getimage, palette, &w, &h, &pixels);
225   /* only the first process handles the saving business */
226   if (rank == 0) PetscCall(PetscDrawImageSave(basename, draw->saveimageext, palette, w, h, pixels));
227   PetscCall(PetscFree(pixels));
228   PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)draw)));
229 
230 finally:
231 #if defined(PETSC_HAVE_SAWS)
232   PetscCall(PetscDrawSave_SAWs(draw));
233 #endif
234   PetscFunctionReturn(0);
235 }
236 
237 /*@
238    PetscDrawSaveMovie - Saves a movie from previously saved images
239 
240    Collective on draw
241 
242    Input Parameters:
243 .  draw - the drawing context
244 
245    Level: advanced
246 
247    Notes:
248    This is not normally called by the user.
249 
250    The ffmpeg utility must be in your path to make the movie.
251 
252 .seealso: `PetscDraw`, `PetscDrawSetSave()`, `PetscDrawSetSaveMovie()`
253 @*/
254 PetscErrorCode PetscDrawSaveMovie(PetscDraw draw)
255 {
256   PetscMPIInt rank;
257 
258   PetscFunctionBegin;
259   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
260   if (!draw->ops->save && !draw->ops->getimage) PetscFunctionReturn(0);
261   if (!draw->savefilename || !draw->savemovieext || draw->savesinglefile) PetscFunctionReturn(0);
262   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)draw), &rank));
263   {
264     const char *fname = draw->savefilename;
265     const char *imext = draw->saveimageext;
266     const char *mvext = draw->savemovieext;
267     if (rank == 0) PetscCall(PetscDrawMovieSave(fname, draw->savefilecount, imext, draw->savemoviefps, mvext));
268     PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)draw)));
269   }
270   PetscFunctionReturn(0);
271 }
272 
273 #if defined(PETSC_HAVE_SAWS)
274   #include <petscviewersaws.h>
275 /*
276   The PetscImageList object and functions are used to maintain a list of file images
277   that can be displayed by the SAWs webserver.
278 */
279 typedef struct _P_PetscImageList *PetscImageList;
280 struct _P_PetscImageList {
281   PetscImageList next;
282   char          *filename;
283   char          *ext;
284   PetscInt       count;
285 };
286 
287 static PetscImageList SAWs_images = NULL;
288 
289 static PetscErrorCode PetscImageListDestroy(void)
290 {
291   PetscImageList image = SAWs_images;
292 
293   PetscFunctionBegin;
294   while (image) {
295     PetscImageList next = image->next;
296     PetscCall(PetscFree(image->filename));
297     PetscCall(PetscFree(image->ext));
298     PetscCall(PetscFree(image));
299     image = next;
300   }
301   PetscFunctionReturn(0);
302 }
303 
304 static PetscErrorCode PetscImageListAdd(const char filename[], const char ext[], PetscInt count)
305 {
306   PetscImageList image, oimage = SAWs_images;
307   PetscBool      flg;
308 
309   PetscFunctionBegin;
310   if (oimage) {
311     PetscCall(PetscStrcmp(filename, oimage->filename, &flg));
312     if (flg) {
313       oimage->count = count;
314       PetscFunctionReturn(0);
315     }
316     while (oimage->next) {
317       oimage = oimage->next;
318       PetscCall(PetscStrcmp(filename, oimage->filename, &flg));
319       if (flg) {
320         oimage->count = count;
321         PetscFunctionReturn(0);
322       }
323     }
324     PetscCall(PetscNew(&image));
325     oimage->next = image;
326   } else {
327     PetscCall(PetscRegisterFinalize(PetscImageListDestroy));
328     PetscCall(PetscNew(&image));
329     SAWs_images = image;
330   }
331   PetscCall(PetscStrallocpy(filename, &image->filename));
332   PetscCall(PetscStrallocpy(ext, &image->ext));
333   image->count = count;
334   PetscFunctionReturn(0);
335 }
336 
337 static PetscErrorCode PetscDrawSave_SAWs(PetscDraw draw)
338 {
339   PetscImageList image;
340   char           body[4096];
341   size_t         len = 0;
342 
343   PetscFunctionBegin;
344   if (!draw->savefilename || !draw->saveimageext) PetscFunctionReturn(0);
345   PetscCall(PetscImageListAdd(draw->savefilename, draw->saveimageext, draw->savefilecount - 1));
346   image = SAWs_images;
347   while (image) {
348     const char *name = image->filename;
349     const char *ext  = image->ext;
350     if (draw->savesinglefile) {
351       PetscCall(PetscSNPrintf(body + len, 4086 - len, "<img src=\"%s%s\" alt=\"None\">", name, ext));
352     } else {
353       PetscCall(PetscSNPrintf(body + len, 4086 - len, "<img src=\"%s/%s_%d%s\" alt=\"None\">", name, name, image->count, ext));
354     }
355     PetscCall(PetscStrlen(body, &len));
356     image = image->next;
357   }
358   PetscCall(PetscStrlcat(body, "<br>\n", sizeof(body)));
359   if (draw->savefilecount > 0) PetscCallSAWs(SAWs_Pop_Body, ("index.html", 1));
360   PetscCallSAWs(SAWs_Push_Body, ("index.html", 1, body));
361   PetscFunctionReturn(0);
362 }
363 
364 #endif
365