// 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; }
WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux, int width, int height) { WebPMuxError err; if (mux == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } if (width < 0 || height < 0 || width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { return WEBP_MUX_INVALID_ARGUMENT; } if (width * (uint64_t)height >= MAX_IMAGE_AREA) { return WEBP_MUX_INVALID_ARGUMENT; } if ((width * height) == 0 && (width | height) != 0) { // one of width / height is zero, but not both -> invalid! return WEBP_MUX_INVALID_ARGUMENT; } // If we already assembled a VP8X chunk, invalidate it. err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; mux->canvas_width_ = width; mux->canvas_height_ = height; return WEBP_MUX_OK; }
WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4], const WebPData* chunk_data, int copy_data) { uint32_t tag; WebPMuxError err; if (mux == NULL || fourcc == NULL || chunk_data == NULL || chunk_data->bytes == NULL || chunk_data->size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } tag = ChunkGetTagFromFourCC(fourcc); // Delete existing chunk(s) with the same 'fourcc'. err = MuxDeleteAllNamedData(mux, tag); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given chunk. return MuxSet(mux, tag, 1, chunk_data, copy_data); }
WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux, const WebPMuxAnimParams* params) { WebPMuxError err; uint8_t data[ANIM_CHUNK_SIZE]; if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT; if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) { return WEBP_MUX_INVALID_ARGUMENT; } // Delete any existing ANIM chunk(s). err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Set the animation parameters. PutLE32(data, params->bgcolor); PutLE16(data + 4, params->loop_count); return MuxAddChunk(mux, 1, kChunks[IDX_ANIM].tag, data, sizeof(data), 1); }
// VP8X format: // Total Size : 10, // Flags : 4 bytes, // Width : 3 bytes, // Height : 3 bytes. static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { WebPMuxError err = WEBP_MUX_OK; uint32_t flags = 0; int width = 0; int height = 0; uint8_t data[VP8X_CHUNK_SIZE]; const WebPData vp8x = { data, VP8X_CHUNK_SIZE }; const WebPMuxImage* images = NULL; assert(mux != NULL); images = mux->images_; // First image. if (images == NULL || images->img_ == NULL || images->img_->data_.bytes == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } // If VP8X chunk(s) is(are) already present, remove them (and later add new // VP8X chunk with updated flags). err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Set flags. if (mux->iccp_ != NULL && mux->iccp_->data_.bytes != NULL) { flags |= ICCP_FLAG; } if (mux->exif_ != NULL && mux->exif_->data_.bytes != NULL) { flags |= EXIF_FLAG; } if (mux->xmp_ != NULL && mux->xmp_->data_.bytes != NULL) { flags |= XMP_FLAG; } if (images->header_ != NULL) { if (images->header_->tag_ == kChunks[IDX_FRGM].tag) { // This is a fragmented image. flags |= FRAGMENTS_FLAG; } else if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } } if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) { flags |= ALPHA_FLAG; // Some images have an alpha channel. } if (flags == 0) { // For Simple Image, VP8X chunk should not be added. return WEBP_MUX_OK; } err = GetImageCanvasWidthHeight(mux, flags, &width, &height); if (err != WEBP_MUX_OK) return err; if (width <= 0 || height <= 0) { return WEBP_MUX_INVALID_ARGUMENT; } if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { return WEBP_MUX_INVALID_ARGUMENT; } if (MuxHasAlpha(images)) { // This means some frames explicitly/implicitly contain alpha. // Note: This 'flags' update must NOT be done for a lossless image // without a VP8X chunk! flags |= ALPHA_FLAG; } PutLE32(data + 0, flags); // VP8X chunk flags. PutLE24(data + 4, width - 1); // canvas width. PutLE24(data + 7, height - 1); // canvas height. return MuxSet(mux, kChunks[IDX_VP8X].tag, 1, &vp8x, 1); }
WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) { if (mux == NULL || fourcc == NULL) return WEBP_MUX_INVALID_ARGUMENT; return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc)); }
// VP8X format: // Total Size : 10, // Flags : 4 bytes, // Width : 3 bytes, // Height : 3 bytes. static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { WebPMuxError err = WEBP_MUX_OK; uint32_t flags = 0; int width = 0; int height = 0; uint8_t data[VP8X_CHUNK_SIZE]; const size_t data_size = VP8X_CHUNK_SIZE; const WebPMuxImage* images = NULL; assert(mux != NULL); images = mux->images_; // First image. if (images == NULL || images->img_ == NULL || images->img_->data_.bytes_ == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } // If VP8X chunk(s) is(are) already present, remove them (and later add new // VP8X chunk with updated flags). err = MuxDeleteAllNamedData(mux, IDX_VP8X); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Set flags. if (mux->iccp_ != NULL && mux->iccp_->data_.bytes_ != NULL) { flags |= ICCP_FLAG; } if (mux->meta_ != NULL && mux->meta_->data_.bytes_ != NULL) { flags |= META_FLAG; } if (images->header_ != NULL) { if (images->header_->tag_ == kChunks[IDX_TILE].tag) { // This is a tiled image. flags |= TILE_FLAG; } else if (images->header_->tag_ == kChunks[IDX_FRAME].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } } if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) { flags |= ALPHA_FLAG; // Some images have an alpha channel. } if (flags == 0) { // For Simple Image, VP8X chunk should not be added. return WEBP_MUX_OK; } err = GetImageCanvasWidthHeight(mux, flags, &width, &height); if (err != WEBP_MUX_OK) return err; if (width <= 0 || height <= 0) { return WEBP_MUX_INVALID_ARGUMENT; } if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { return WEBP_MUX_INVALID_ARGUMENT; } if (MuxHasLosslessImages(images)) { // We have a file with a VP8X chunk having some lossless images. // As lossless images implicitly contain alpha, force ALPHA_FLAG to be true. // Note: This 'flags' update must NOT be done for a lossless image // without a VP8X chunk! flags |= ALPHA_FLAG; } PutLE32(data + 0, flags); // VP8X chunk flags. PutLE24(data + 4, width - 1); // canvas width. PutLE24(data + 7, height - 1); // canvas height. err = MuxAddChunk(mux, 1, kChunks[IDX_VP8X].tag, data, data_size, 1); return err; }
WebPMuxError WebPMuxDeleteColorProfile(WebPMux* mux) { return MuxDeleteAllNamedData(mux, IDX_ICCP); }
WebPMuxError WebPMuxDeleteMetadata(WebPMux* mux) { return MuxDeleteAllNamedData(mux, IDX_META); }
static WebPMuxError DeleteLoopCount(WebPMux* const mux) { return MuxDeleteAllNamedData(mux, IDX_LOOP); }