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