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