Actual source code: image.c

      1: #include <petsc/private/petscimpl.h>

      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 *[]);

      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;

     18:   PetscFunctionBegin;
     19:   PetscAssertPointer(filename, 1);
     20:   if (palette) PetscAssertPointer(palette, 2);
     21:   PetscAssertPointer(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: }

     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: }

     53: /*
     54:    Code to write images in PNG format
     55: */
     56: #if defined(PETSC_HAVE_LIBPNG)

     58:   #include <png.h>

     60:   #if defined(PNG_SETJMP_SUPPORTED)
     61:     #if !defined(png_jmpbuf)
     62:       #define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
     63:     #endif
     64:   #endif

     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;

     73:   PetscFunctionBegin;
     74:   PetscAssertPointer(filename, 1);
     75:   if (palette) PetscAssertPointer(palette, 2);
     76:   PetscAssertPointer(pixels, 5);

     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");

     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

     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);

     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);

    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: }

    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: }

    115: #endif /*!PETSC_HAVE_LIBPNG*/

    117: /*
    118:    Code to write images in GIF format
    119: */
    120: #if defined(PETSC_HAVE_GIFLIB)

    122:   #include <gif_lib.h>

    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

    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)

    151:   PetscFunctionBegin;
    152:   PetscAssertPointer(filename, 1);
    153:   PetscAssertPointer(palette, 2);
    154:   PetscAssertPointer(pixels, 5);

    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;

    167:   #undef SETERRGIF
    168:   #undef CHKERRGIF
    169:   PetscFunctionReturn(PETSC_SUCCESS);
    170: }

    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: }

    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)

    185:   PetscFunctionBegin;
    186:   PetscAssertPointer(pattern, 1);
    187:   PetscAssertPointer(movie, 3);
    188:   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);

    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);

    216:   #undef SETERRGIF
    217:   PetscFunctionReturn(PETSC_SUCCESS);
    218: }

    220: #endif /*!PETSC_HAVE_GIFLIB*/

    222: /*
    223:    Code to write images in JPEG format
    224: */
    225: #if defined(PETSC_HAVE_LIBJPEG)

    227:   #include <jpeglib.h>

    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

    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;

    246:   PetscFunctionBegin;
    247:   PetscAssertPointer(filename, 1);
    248:   if (palette) PetscAssertPointer(palette, 2);
    249:   PetscAssertPointer(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));

    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);

    292:   PetscCall(PetscFClose(PETSC_COMM_SELF, fp));
    293:   if (palette) PetscCall(PetscFree(rgbpixels));
    294:   PetscFunctionReturn(PETSC_SUCCESS);
    295: }

    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: }

    302: #endif /*!PETSC_HAVE_LIBJPEG*/

    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: };

    320: PetscErrorCode PetscDrawImageCheckFormat(const char *ext[])
    321: {
    322:   size_t    k;
    323:   PetscBool match = PETSC_FALSE;

    325:   PetscFunctionBegin;
    326:   /* if extension is empty, return default format to caller */
    327:   PetscAssertPointer(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:   PetscAssertPointer(*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: }

    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];

    347:   PetscFunctionBegin;
    348:   PetscAssertPointer(basename, 1);
    349:   if (ext) PetscAssertPointer(ext, 2);
    350:   if (palette) PetscAssertPointer(palette, 3);
    351:   PetscAssertPointer(pixels, 6);

    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: }

    365: PetscErrorCode PetscDrawMovieCheckFormat(const char *ext[])
    366: {
    367:   PetscFunctionBegin;
    368:   PetscAssertPointer(ext, 1);
    369:   if (!*ext || !**ext) *ext = ".m4v";
    370:   PetscFunctionReturn(PETSC_SUCCESS);
    371: }

    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;

    379:   PetscFunctionBegin;
    380:   PetscAssertPointer(basename, 1);
    381:   PetscAssertPointer(imext, 3);
    382:   if (mvext) PetscAssertPointer(mvext, 5);
    383:   if (count < 1) PetscFunctionReturn(PETSC_SUCCESS);

    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));

    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

    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 %" PetscInt_FMT, fps));
    407:     if (gifinput) {
    408:       PetscCall(PetscStrlcat(options, " -f gif", sizeof(options)));
    409:       PetscCall(PetscSNPrintf(extraopts, sizeof(extraopts), " -default_delay %" PetscInt_FMT, (fps > 0) ? 100 / fps : 4));
    410:     } else {
    411:       PetscCall(PetscStrlcat(options, " -f image2", sizeof(options)));
    412:       if (fps > 0) PetscCall(PetscSNPrintf(extraopts, sizeof(extraopts), " -framerate %" PetscInt_FMT, 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: }