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