// Create data for frame/fragment given image data, offsets and duration. static WebPMuxError CreateFrameFragmentData( int width, int height, const WebPMuxFrameInfo* const info, int is_frame, WebPData* const frame_frgm) { uint8_t* frame_frgm_bytes; const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; assert(width > 0 && height > 0 && info->duration >= 0); assert(info->dispose_method == (info->dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size); if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_frgm_bytes + 0, info->x_offset / 2); PutLE24(frame_frgm_bytes + 3, info->y_offset / 2); if (is_frame) { PutLE24(frame_frgm_bytes + 6, width - 1); PutLE24(frame_frgm_bytes + 9, height - 1); PutLE24(frame_frgm_bytes + 12, info->duration); frame_frgm_bytes[15] = (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); } frame_frgm->bytes = frame_frgm_bytes; frame_frgm->size = frame_frgm_size; return WEBP_MUX_OK; }
// Create data for frame/tile given image data, offsets and duration. static WebPMuxError CreateFrameTileData(const WebPData* const image, int x_offset, int y_offset, int duration, int is_lossless, int is_frame, WebPData* const frame_tile) { int width; int height; uint8_t* frame_tile_bytes; const size_t frame_tile_size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size; const int ok = is_lossless ? VP8LGetInfo(image->bytes_, image->size_, &width, &height, NULL) : VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height); if (!ok) return WEBP_MUX_INVALID_ARGUMENT; assert(width > 0 && height > 0 && duration > 0); // Note: assertion on upper bounds is done in PutLE24(). frame_tile_bytes = (uint8_t*)malloc(frame_tile_size); if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_tile_bytes + 0, x_offset / 2); PutLE24(frame_tile_bytes + 3, y_offset / 2); if (is_frame) { PutLE24(frame_tile_bytes + 6, width - 1); PutLE24(frame_tile_bytes + 9, height - 1); PutLE24(frame_tile_bytes + 12, duration - 1); } frame_tile->bytes_ = frame_tile_bytes; frame_tile->size_ = frame_tile_size; return WEBP_MUX_OK; }
// Create data for frame/fragment given image data, offsets and duration. static WebPMuxError CreateFrameFragmentData( const WebPData* const image, int x_offset, int y_offset, int duration, WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame, WebPData* const frame_frgm) { int width; int height; uint8_t* frame_frgm_bytes; const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; const int ok = is_lossless ? VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) : VP8GetInfo(image->bytes, image->size, image->size, &width, &height); if (!ok) return WEBP_MUX_INVALID_ARGUMENT; assert(width > 0 && height > 0 && duration >= 0); assert(dispose_method == (dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size); if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_frgm_bytes + 0, x_offset / 2); PutLE24(frame_frgm_bytes + 3, y_offset / 2); if (is_frame) { PutLE24(frame_frgm_bytes + 6, width - 1); PutLE24(frame_frgm_bytes + 9, height - 1); PutLE24(frame_frgm_bytes + 12, duration); frame_frgm_bytes[15] = (dispose_method & 1); } frame_frgm->bytes = frame_frgm_bytes; frame_frgm->size = frame_frgm_size; return WEBP_MUX_OK; }
static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width, int height, uint32_t flags) { const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; assert(width >= 1 && height >= 1); assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE); assert(width * (uint64_t)height < MAX_IMAGE_AREA); PutLE32(dst, MKFOURCC('V', 'P', '8', 'X')); PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE); PutLE32(dst + CHUNK_HEADER_SIZE, flags); PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1); PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1); return dst + vp8x_size; }
// 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); }
// 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; }