// Cleans up 'mux' by removing any unnecessary chunks. static WebPMuxError MuxCleanup(WebPMux* const mux) { int num_frames; int num_anim_chunks; // If we have an image with a single frame, and its rectangle // covers the whole canvas, convert it to a non-animated image // (to avoid writing ANMF chunk unnecessarily). WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames); if (err != WEBP_MUX_OK) return err; if (num_frames == 1) { WebPMuxImage* frame = NULL; err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame); assert(err == WEBP_MUX_OK); // We know that one frame does exist. assert(frame != NULL); if (frame->header_ != NULL && ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) || (frame->width_ == mux->canvas_width_ && frame->height_ == mux->canvas_height_))) { assert(frame->header_->tag_ == kChunks[IDX_ANMF].tag); ChunkDelete(frame->header_); // Removes ANMF chunk. frame->header_ = NULL; num_frames = 0; } } // Remove ANIM chunk if this is a non-animated image. err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks); if (err != WEBP_MUX_OK) return err; if (num_anim_chunks >= 1 && num_frames == 0) { err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag); if (err != WEBP_MUX_OK) return err; } return WEBP_MUX_OK; }
// Cleans up 'mux' by removing any unnecessary chunks. static WebPMuxError MuxCleanup(WebPMux* const mux) { int num_frames; int num_fragments; int num_anim_chunks; // If we have an image with single fragment or frame, convert it to a // non-animated non-fragmented image (to avoid writing FRGM/ANMF chunk // unnecessarily). WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames); if (err != WEBP_MUX_OK) return err; err = WebPMuxNumChunks(mux, kChunks[IDX_FRGM].id, &num_fragments); if (err != WEBP_MUX_OK) return err; if (num_frames == 1 || num_fragments == 1) { WebPMuxImage* frame_frag; err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame_frag); assert(err == WEBP_MUX_OK); // We know that one frame/fragment does exist. if (frame_frag->header_ != NULL) { assert(frame_frag->header_->tag_ == kChunks[IDX_ANMF].tag || frame_frag->header_->tag_ == kChunks[IDX_FRGM].tag); ChunkDelete(frame_frag->header_); // Removes ANMF/FRGM chunk. frame_frag->header_ = NULL; } num_frames = 0; num_fragments = 0; } // Remove ANIM chunk if this is a non-animated image. err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks); if (err != WEBP_MUX_OK) return err; if (num_anim_chunks >= 1 && num_frames == 0) { err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag); if (err != WEBP_MUX_OK) return err; } return WEBP_MUX_OK; }
// Test basic constraints: // retrieval, maximum number of chunks by index (use -1 to skip) // and feature incompatibility (use NO_FLAG to skip). // On success returns WEBP_MUX_OK and stores the chunk count in *num. static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, WebPFeatureFlags feature, uint32_t vp8x_flags, int max, int* num) { const WebPMuxError err = WebPMuxNumChunks(mux, kChunks[idx].id, num); if (err != WEBP_MUX_OK) return err; if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT; if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) { return WEBP_MUX_INVALID_ARGUMENT; } return WEBP_MUX_OK; }
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; }
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; }
WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) { size_t size = 0; uint8_t* data = NULL; uint8_t* dst = NULL; int num_frames; int num_loop_chunks; WebPMuxError err; if (mux == NULL || assembled_data == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } // Remove LOOP chunk if unnecessary. err = WebPMuxNumChunks(mux, kChunks[IDX_LOOP].id, &num_loop_chunks); if (err != WEBP_MUX_OK) return err; if (num_loop_chunks >= 1) { err = WebPMuxNumChunks(mux, kChunks[IDX_FRAME].id, &num_frames); if (err != WEBP_MUX_OK) return err; if (num_frames == 0) { err = DeleteLoopCount(mux); if (err != WEBP_MUX_OK) return err; } } // Create VP8X chunk. err = CreateVP8XChunk(mux); if (err != WEBP_MUX_OK) return err; // Allocate data. size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_) + ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_) + ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE; data = (uint8_t*)malloc(size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; // Emit header & chunks. dst = MuxEmitRiffHeader(data, size); dst = ChunkListEmit(mux->vp8x_, dst); dst = ChunkListEmit(mux->iccp_, dst); dst = ChunkListEmit(mux->loop_, dst); dst = MuxImageListEmit(mux->images_, dst); dst = ChunkListEmit(mux->meta_, dst); dst = ChunkListEmit(mux->unknown_, dst); assert(dst == data + size); // Validate mux. err = MuxValidate(mux); if (err != WEBP_MUX_OK) { free(data); data = NULL; size = 0; } // Finalize. assembled_data->bytes_ = data; assembled_data->size_ = size; return err; }