static WebPMuxError DisplayInfo(const WebPMux* mux) { int width, height; uint32_t flag; WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height); assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier. printf("Canvas size: %d x %d\n", width, height); err = WebPMuxGetFeatures(mux, &flag); if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT; RETURN_IF_ERROR("Failed to retrieve features\n"); if (flag == 0) { fprintf(stderr, "No features present.\n"); return err; } // Print the features present. printf("Features present:"); if (flag & ANIMATION_FLAG) printf(" animation"); if (flag & FRAGMENTS_FLAG) printf(" image fragments"); if (flag & ICCP_FLAG) printf(" ICC profile"); if (flag & EXIF_FLAG) printf(" EXIF metadata"); if (flag & XMP_FLAG) printf(" XMP metadata"); if (flag & ALPHA_FLAG) printf(" transparency"); printf("\n"); if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) { const int is_anim = !!(flag & ANIMATION_FLAG); const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM; const char* const type_str = is_anim ? "frame" : "fragment"; int nFrames; if (is_anim) { WebPMuxAnimParams params; err = WebPMuxGetAnimationParams(mux, ¶ms); assert(err == WEBP_MUX_OK); printf("Background color : 0x%.8X Loop Count : %d\n", params.bgcolor, params.loop_count); } err = WebPMuxNumChunks(mux, id, &nFrames); assert(err == WEBP_MUX_OK); printf("Number of %ss: %d\n", type_str, nFrames); if (nFrames > 0) { int i; printf("No.: width height alpha x_offset y_offset "); if (is_anim) printf("duration dispose blend "); printf("image_size\n"); for (i = 1; i <= nFrames; i++) { WebPMuxFrameInfo frame; err = WebPMuxGetFrame(mux, i, &frame); if (err == WEBP_MUX_OK) { WebPBitstreamFeatures features; const VP8StatusCode status = WebPGetFeatures( frame.bitstream.bytes, frame.bitstream.size, &features); assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate(). (void)status; printf("%3d: %5d %5d %5s %8d %8d ", i, features.width, features.height, features.has_alpha ? "yes" : "no", frame.x_offset, frame.y_offset); if (is_anim) { const char* const dispose = (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" : "background"; const char* const blend = (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no"; printf("%8d %10s %5s ", frame.duration, dispose, blend); } printf("%10d\n", (int)frame.bitstream.size); } WebPDataClear(&frame.bitstream); RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); } } } if (flag & ICCP_FLAG) { WebPData icc_profile; err = WebPMuxGetChunk(mux, "ICCP", &icc_profile); assert(err == WEBP_MUX_OK); printf("Size of the ICC profile data: %d\n", (int)icc_profile.size); } if (flag & EXIF_FLAG) { WebPData exif; err = WebPMuxGetChunk(mux, "EXIF", &exif); assert(err == WEBP_MUX_OK); printf("Size of the EXIF metadata: %d\n", (int)exif.size); } if (flag & XMP_FLAG) { WebPData xmp; err = WebPMuxGetChunk(mux, "XMP ", &xmp); assert(err == WEBP_MUX_OK); printf("Size of the XMP metadata: %d\n", (int)xmp.size); } if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) { WebPMuxFrameInfo image; err = WebPMuxGetFrame(mux, 1, &image); if (err == WEBP_MUX_OK) { printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size); } WebPDataClear(&image.bitstream); RETURN_IF_ERROR("Failed to retrieve the image\n"); } return WEBP_MUX_OK; }
static WebPMuxError DisplayInfo(const WebPMux* mux) { uint32_t flag; WebPMuxError err = WebPMuxGetFeatures(mux, &flag); #ifndef WEBP_EXPERIMENTAL_FEATURES if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT; #endif RETURN_IF_ERROR("Failed to retrieve features\n"); if (flag == 0) { fprintf(stderr, "No features present.\n"); return err; } // Print the features present. printf("Features present:"); if (flag & ANIMATION_FLAG) printf(" animation"); if (flag & FRAGMENTS_FLAG) printf(" image fragments"); if (flag & ICCP_FLAG) printf(" ICC profile"); if (flag & EXIF_FLAG) printf(" EXIF metadata"); if (flag & XMP_FLAG) printf(" XMP metadata"); if (flag & ALPHA_FLAG) printf(" transparency"); printf("\n"); if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) { const int is_anim = !!(flag & ANIMATION_FLAG); const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM; const char* const type_str = is_anim ? "frame" : "fragment"; int nFrames; if (is_anim) { WebPMuxAnimParams params; err = WebPMuxGetAnimationParams(mux, ¶ms); RETURN_IF_ERROR("Failed to retrieve animation parameters\n"); printf("Background color : 0x%.8X Loop Count : %d\n", params.bgcolor, params.loop_count); } err = WebPMuxNumChunks(mux, id, &nFrames); RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str); printf("Number of %ss: %d\n", type_str, nFrames); if (nFrames > 0) { int i; printf("No.: x_offset y_offset "); if (is_anim) printf("duration dispose "); printf("image_size\n"); for (i = 1; i <= nFrames; i++) { WebPMuxFrameInfo frame; err = WebPMuxGetFrame(mux, i, &frame); if (err == WEBP_MUX_OK) { printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset); if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method); printf("%10d\n", (int)frame.bitstream.size); } WebPDataClear(&frame.bitstream); RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); } } } if (flag & ICCP_FLAG) { WebPData icc_profile; err = WebPMuxGetChunk(mux, "ICCP", &icc_profile); RETURN_IF_ERROR("Failed to retrieve the ICC profile\n"); printf("Size of the ICC profile data: %d\n", (int)icc_profile.size); } if (flag & EXIF_FLAG) { WebPData exif; err = WebPMuxGetChunk(mux, "EXIF", &exif); RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n"); printf("Size of the EXIF metadata: %d\n", (int)exif.size); } if (flag & XMP_FLAG) { WebPData xmp; err = WebPMuxGetChunk(mux, "XMP ", &xmp); RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n"); printf("Size of the XMP metadata: %d\n", (int)xmp.size); } if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) { WebPMuxFrameInfo image; err = WebPMuxGetFrame(mux, 1, &image); if (err == WEBP_MUX_OK) { printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size); } WebPDataClear(&image.bitstream); RETURN_IF_ERROR("Failed to retrieve the image\n"); } return WEBP_MUX_OK; }
WebPMuxError MuxValidate(const WebPMux* const mux) { int num_iccp; int num_exif; int num_xmp; int num_anim; int num_frames; int num_fragments; int num_vp8x; int num_images; int num_alpha; uint32_t flags; WebPMuxError err; // Verify mux is not NULL. if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; // Verify mux has at least one image. if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT; err = WebPMuxGetFeatures(mux, &flags); if (err != WEBP_MUX_OK) return err; // At most one color profile chunk. err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp); if (err != WEBP_MUX_OK) return err; // At most one EXIF metadata. err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif); if (err != WEBP_MUX_OK) return err; // At most one XMP metadata. err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp); if (err != WEBP_MUX_OK) return err; // Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent. // At most one ANIM chunk. err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim); if (err != WEBP_MUX_OK) return err; err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames); if (err != WEBP_MUX_OK) return err; { const int has_animation = !!(flags & ANIMATION_FLAG); if (has_animation && (num_anim == 0 || num_frames == 0)) { return WEBP_MUX_INVALID_ARGUMENT; } if (!has_animation && (num_anim == 1 || num_frames > 0)) { return WEBP_MUX_INVALID_ARGUMENT; } } // Fragmentation: FRAGMENTS_FLAG and FRGM chunk(s) are consistent. err = ValidateChunk(mux, IDX_FRGM, FRAGMENTS_FLAG, flags, -1, &num_fragments); if (err != WEBP_MUX_OK) return err; // Verify either VP8X chunk is present OR there is only one elem in // mux->images_. err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x); if (err != WEBP_MUX_OK) return err; err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images); if (err != WEBP_MUX_OK) return err; if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT; // ALPHA_FLAG & alpha chunk(s) are consistent. if (MuxHasAlpha(mux->images_)) { if (num_vp8x > 0) { // VP8X chunk is present, so it should contain ALPHA_FLAG. if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT; } else { // VP8X chunk is not present, so ALPH chunks should NOT be present either. err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha); if (err != WEBP_MUX_OK) return err; if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT; } } else { // Mux doesn't need alpha. So, ALPHA_FLAG should NOT be present. if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT; } // num_fragments & num_images are consistent. if (num_fragments > 0 && num_images != num_fragments) { return WEBP_MUX_INVALID_ARGUMENT; } return WEBP_MUX_OK; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { WebPMux *mux = NULL; WebPMuxFrameInfo webp_frame = { 0 }; // raw image WebPData color_profile; // ICC raw data WebPData xmp_metadata; // XMP raw data WebPData exif_metadata; // EXIF raw data FIBITMAP *dib = NULL; WebPMuxError error_status; if(!handle) { return NULL; } try { // get the MUX object mux = (WebPMux*)data; if(!mux) { throw (1); } // gets the feature flags from the mux object uint32_t webp_flags = 0; error_status = WebPMuxGetFeatures(mux, &webp_flags); if(error_status != WEBP_MUX_OK) { throw (1); } // get image data error_status = WebPMuxGetFrame(mux, 1, &webp_frame); if(error_status == WEBP_MUX_OK) { // decode the data (can be limited to the header if flags uses FIF_LOAD_NOPIXELS) dib = DecodeImage(&webp_frame.bitstream, flags); if(!dib) { throw (1); } // get ICC profile if(webp_flags & ICCP_FLAG) { error_status = WebPMuxGetChunk(mux, "ICCP", &color_profile); if(error_status == WEBP_MUX_OK) { FreeImage_CreateICCProfile(dib, (void*)color_profile.bytes, (long)color_profile.size); } } // get XMP metadata if(webp_flags & XMP_FLAG) { error_status = WebPMuxGetChunk(mux, "XMP ", &xmp_metadata); if(error_status == WEBP_MUX_OK) { // create a tag FITAG *tag = FreeImage_CreateTag(); if(tag) { FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetTagLength(tag, (DWORD)xmp_metadata.size); FreeImage_SetTagCount(tag, (DWORD)xmp_metadata.size); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, xmp_metadata.bytes); // store the tag FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } } } // get Exif metadata if(webp_flags & EXIF_FLAG) { error_status = WebPMuxGetChunk(mux, "EXIF", &exif_metadata); if(error_status == WEBP_MUX_OK) { // read the Exif raw data as a blob jpeg_read_exif_profile_raw(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); // read and decode the Exif data jpeg_read_exif_profile(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); } } } WebPDataClear(&webp_frame.bitstream); return dib; } catch(int) { WebPDataClear(&webp_frame.bitstream); return NULL; } }
gboolean load_image(const gchar *filename, gint32 *image_ID, GError **error) { gboolean status = FALSE; gchar *indata = NULL; gsize indatalen; gint width; gint height; WebPMux *mux = NULL; WebPData wp_data; uint32_t flags; uint8_t *outdata = NULL; #ifdef GIMP_2_9 /* Initialize GEGL */ gegl_init(NULL, NULL); #endif do { /* Attempt to read the file contents from disk */ if (g_file_get_contents(filename, &indata, &indatalen, error) == FALSE) { break; } /* Validate WebP data, grabbing the width and height */ if (!WebPGetInfo(indata, indatalen, &width, &height)) { break; } /* Create a WebPMux from the contents of the file */ wp_data.bytes = (uint8_t*)indata; wp_data.size = indatalen; mux = WebPMuxCreate(&wp_data, 1); if (mux == NULL) { break; } /* Retrieve the features present */ if (WebPMuxGetFeatures(mux, &flags) != WEBP_MUX_OK) { break; } /* TODO: decode the image in "chunks" or "tiles" */ /* TODO: check if an alpha channel is present */ /* Create the new image and associated layer */ *image_ID = gimp_image_new(width, height, GIMP_RGB); #ifdef WEBP_0_5 if (flags & ANIMATION_FLAG) { int frames, i; /* Retrieve the number of frames */ WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &frames); /* Loop over each of the frames */ for (i = 0; i < frames; ++i) { WebPMuxFrameInfo frame = {0}; /* Retrieve the data for the frame */ if (WebPMuxGetFrame(mux, i, &frame) != WEBP_MUX_OK) { goto error; } /* Decode the frame */ outdata = WebPDecodeRGBA(frame.bitstream.bytes, frame.bitstream.size, &width, &height); /* Free the compressed data */ WebPDataClear(&frame.bitstream); if (!outdata) { goto error; } /* Create a layer for the frame */ char name[255]; snprintf(name, 255, "Frame %d", (i + 1)); if (create_layer(*image_ID, outdata, 0, (gchar*)name, width, height, frame.x_offset, frame.y_offset) == FALSE) { goto error; } } /* If all is well, jump *over* the error label - otherwise leave the loop and begin cleaning things up */ goto success; error: break; success: ; } else { #endif /* Attempt to decode the data as a WebP image */ outdata = WebPDecodeRGBA(indata, indatalen, &width, &height); if (!outdata) { break; } /* Create a single layer */ status = create_layer(*image_ID, outdata, 0, "Background", width, height, 0, 0); #ifdef WEBP_0_5 } #ifdef GIMP_2_9 /* Load a color profile if one was provided */ if (flags & ICCP_FLAG) { WebPData icc_profile; GimpColorProfile *profile; /* Load the ICC profile from the file */ WebPMuxGetChunk(mux, "ICCP", &icc_profile); /* Have Gimp load the color profile */ profile = gimp_color_profile_new_from_icc_profile( icc_profile.bytes, icc_profile.size, NULL); if (profile) { gimp_image_set_color_profile(image_ID, profile); g_object_unref(profile); } } #endif #endif /* Set the filename for the image */ gimp_image_set_filename(*image_ID, filename); } while(0); /* Delete the mux object */ if (mux) { WebPMuxDelete(mux); } /* Free the data read from disk */ if (indata) { g_free(indata); } return status; }