xref: /petsc/src/sys/classes/draw/interface/dsave.c (revision f1580f4e3ce5d5b2393648fd039d0d41b440385d)
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   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 draw
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    Note:
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: `PetscDraw`, `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 draw
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 the name of the 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 
128    The supported image types are .png, .gif, and .ppm (PETSc chooses the default in that order).
129 .vb
130    Support for .png images requires configure --with-libpng.
131    Support for .gif images requires configure --with-giflib.
132    Support for .jpg images requires configure --with-libjpeg.
133    Support for .ppm images is built-in. The PPM format has no compression (640x480 pixels ~ 900 KiB).
134 .ve
135 
136 .seealso: `PetscDraw`, `PetscDrawSetSave()`, `PetscDrawSetFromOptions()`, `PetscDrawCreate()`, `PetscDrawDestroy()`
137 @*/
138 PetscErrorCode PetscDrawSetSaveFinalImage(PetscDraw draw, const char filename[]) {
139   char buf[PETSC_MAX_PATH_LEN];
140 
141   PetscFunctionBegin;
142   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
143   if (!filename || !filename[0]) {
144     if (!draw->savefilename) {
145       PetscCall(PetscObjectGetName((PetscObject)draw, &filename));
146     } else {
147       PetscCall(PetscSNPrintf(buf, sizeof(buf), "%s%s", draw->savefilename, draw->saveimageext));
148       filename = buf;
149     }
150   }
151   PetscCall(PetscFree(draw->savefinalfilename));
152   PetscCall(PetscStrallocpy(filename, &draw->savefinalfilename));
153   PetscFunctionReturn(0);
154 }
155 
156 /*@
157    PetscDrawSave - Saves a drawn image
158 
159    Collective on draw
160 
161    Input Parameters:
162 .  draw - the drawing context
163 
164    Level: advanced
165 
166    Note:
167     this is not normally called by the user.
168 
169 .seealso: `PetscDraw`, `PetscDrawSetSave()`
170 @*/
171 PetscErrorCode PetscDrawSave(PetscDraw draw) {
172   PetscInt       saveindex;
173   char           basename[PETSC_MAX_PATH_LEN];
174   unsigned char  palette[256][3];
175   unsigned int   w, h;
176   unsigned char *pixels = NULL;
177   PetscMPIInt    rank;
178 
179   PetscFunctionBegin;
180   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
181   if (!draw->ops->save && !draw->ops->getimage) PetscFunctionReturn(0);
182   if (draw->ops->save) {
183     PetscUseTypeMethod(draw, save);
184     goto finally;
185   }
186   if (!draw->savefilename || !draw->saveimageext) PetscFunctionReturn(0);
187   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)draw), &rank));
188 
189   saveindex = draw->savefilecount++;
190 
191   if (rank == 0 && !saveindex) {
192     char path[PETSC_MAX_PATH_LEN];
193     if (draw->savesinglefile) {
194       PetscCall(PetscSNPrintf(path, sizeof(path), "%s%s", draw->savefilename, draw->saveimageext));
195       (void)remove(path);
196     } else {
197       PetscCall(PetscSNPrintf(path, sizeof(path), "%s", draw->savefilename));
198       PetscCall(PetscRMTree(path));
199       PetscCall(PetscMkdir(path));
200     }
201     if (draw->savemovieext) {
202       PetscCall(PetscSNPrintf(path, sizeof(path), "%s%s", draw->savefilename, draw->savemovieext));
203       (void)remove(path);
204     }
205   }
206   if (draw->savesinglefile) {
207     PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s", draw->savefilename));
208   } else {
209     char *basefilename;
210 
211     PetscCall(PetscStrrchr(draw->savefilename, '/', (char **)&basefilename));
212     if (basefilename != draw->savefilename) {
213       PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s_%d", draw->savefilename, (int)saveindex));
214     } else {
215       PetscCall(PetscSNPrintf(basename, sizeof(basename), "%s/%s_%d", draw->savefilename, draw->savefilename, (int)saveindex));
216     }
217   }
218 
219   /* this call is collective, only the first process gets the image data */
220   PetscUseTypeMethod(draw, getimage, palette, &w, &h, &pixels);
221   /* only the first process handles the saving business */
222   if (rank == 0) PetscCall(PetscDrawImageSave(basename, draw->saveimageext, palette, w, h, pixels));
223   PetscCall(PetscFree(pixels));
224   PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)draw)));
225 
226 finally:
227 #if defined(PETSC_HAVE_SAWS)
228   PetscCall(PetscDrawSave_SAWs(draw));
229 #endif
230   PetscFunctionReturn(0);
231 }
232 
233 /*@
234    PetscDrawSaveMovie - Saves a movie from previously saved images
235 
236    Collective on draw
237 
238    Input Parameters:
239 .  draw - the drawing context
240 
241    Level: advanced
242 
243    Notes:
244    This is not normally called by the user.
245 
246    The ffmpeg utility must be in your path to make the movie.
247 
248 .seealso: `PetscDraw`, `PetscDrawSetSave()`, `PetscDrawSetSaveMovie()`
249 @*/
250 PetscErrorCode PetscDrawSaveMovie(PetscDraw draw) {
251   PetscMPIInt rank;
252 
253   PetscFunctionBegin;
254   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
255   if (!draw->ops->save && !draw->ops->getimage) PetscFunctionReturn(0);
256   if (!draw->savefilename || !draw->savemovieext || draw->savesinglefile) PetscFunctionReturn(0);
257   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)draw), &rank));
258   {
259     const char *fname = draw->savefilename;
260     const char *imext = draw->saveimageext;
261     const char *mvext = draw->savemovieext;
262     if (rank == 0) PetscCall(PetscDrawMovieSave(fname, draw->savefilecount, imext, draw->savemoviefps, mvext));
263     PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)draw)));
264   }
265   PetscFunctionReturn(0);
266 }
267 
268 #if defined(PETSC_HAVE_SAWS)
269 #include <petscviewersaws.h>
270 /*
271   The PetscImageList object and functions are used to maintain a list of file images
272   that can be displayed by the SAWs webserver.
273 */
274 typedef struct _P_PetscImageList *PetscImageList;
275 struct _P_PetscImageList {
276   PetscImageList next;
277   char          *filename;
278   char          *ext;
279   PetscInt       count;
280 };
281 
282 static PetscImageList SAWs_images = NULL;
283 
284 static PetscErrorCode PetscImageListDestroy(void) {
285   PetscImageList image = SAWs_images;
286 
287   PetscFunctionBegin;
288   while (image) {
289     PetscImageList next = image->next;
290     PetscCall(PetscFree(image->filename));
291     PetscCall(PetscFree(image->ext));
292     PetscCall(PetscFree(image));
293     image = next;
294   }
295   PetscFunctionReturn(0);
296 }
297 
298 static PetscErrorCode PetscImageListAdd(const char filename[], const char ext[], PetscInt count) {
299   PetscImageList image, oimage = SAWs_images;
300   PetscBool      flg;
301 
302   PetscFunctionBegin;
303   if (oimage) {
304     PetscCall(PetscStrcmp(filename, oimage->filename, &flg));
305     if (flg) {
306       oimage->count = count;
307       PetscFunctionReturn(0);
308     }
309     while (oimage->next) {
310       oimage = oimage->next;
311       PetscCall(PetscStrcmp(filename, oimage->filename, &flg));
312       if (flg) {
313         oimage->count = count;
314         PetscFunctionReturn(0);
315       }
316     }
317     PetscCall(PetscNew(&image));
318     oimage->next = image;
319   } else {
320     PetscCall(PetscRegisterFinalize(PetscImageListDestroy));
321     PetscCall(PetscNew(&image));
322     SAWs_images = image;
323   }
324   PetscCall(PetscStrallocpy(filename, &image->filename));
325   PetscCall(PetscStrallocpy(ext, &image->ext));
326   image->count = count;
327   PetscFunctionReturn(0);
328 }
329 
330 static PetscErrorCode PetscDrawSave_SAWs(PetscDraw draw) {
331   PetscImageList image;
332   char           body[4096];
333   size_t         len = 0;
334 
335   PetscFunctionBegin;
336   if (!draw->savefilename || !draw->saveimageext) PetscFunctionReturn(0);
337   PetscCall(PetscImageListAdd(draw->savefilename, draw->saveimageext, draw->savefilecount - 1));
338   image = SAWs_images;
339   while (image) {
340     const char *name = image->filename;
341     const char *ext  = image->ext;
342     if (draw->savesinglefile) {
343       PetscCall(PetscSNPrintf(body + len, 4086 - len, "<img src=\"%s%s\" alt=\"None\">", name, ext));
344     } else {
345       PetscCall(PetscSNPrintf(body + len, 4086 - len, "<img src=\"%s/%s_%d%s\" alt=\"None\">", name, name, image->count, ext));
346     }
347     PetscCall(PetscStrlen(body, &len));
348     image = image->next;
349   }
350   PetscCall(PetscStrlcat(body, "<br>\n", sizeof(body)));
351   if (draw->savefilecount > 0) PetscCallSAWs(SAWs_Pop_Body, ("index.html", 1));
352   PetscCallSAWs(SAWs_Push_Body, ("index.html", 1, body));
353   PetscFunctionReturn(0);
354 }
355 
356 #endif
357