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