From 8d77dc29e14e57b5fe840988c7149193bcb7e53f Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Mon, 21 May 2012 13:40:35 +0530 Subject: [PATCH] Add support for lossless in mux: - Separate out 'CHUNK_INDEX' from 'TAG_ID' (this is to help with the situation where two different tags - "VP8 " and "VP8L" can have the same TAG_ID -> IMAGE_ID). - Some internal methods now take 'CHUNK_INDEX' param instea of 'TAG_ID' as appropriate. - Add kChunks[] entry for lossless. - Rename WebPMuxImage.vp8_ --> WebPMuxImage.img_ - SetImage() and AddFrame/Tile() infer whether the bitstream is a lossless one based on LOSSLESS_MAGIC_BYTE. The correct tag is stored based on this. Also, handle the case when GetVP8Info/GetVP8LInfo() fails. Change-Id: I6b3bc9555cedb791b43f743b5a7770958864bb05 --- src/mux/muxedit.c | 181 +++++++++++++++++++++++++----------------- src/mux/muxi.h | 55 +++++++++---- src/mux/muxinternal.c | 85 +++++++++++--------- src/mux/muxread.c | 69 ++++++++-------- src/webp/mux.h | 16 ++-- 5 files changed, 242 insertions(+), 164 deletions(-) diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index fdf46d14..e99aae8c 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -60,31 +60,31 @@ void WebPMuxDelete(WebPMux* const mux) { // Helper method(s). // Handy MACRO, makes MuxSet() very symmetric to MuxGet(). -#define SWITCH_ID_LIST(ID, LIST) \ - if (id == (ID)) { \ +#define SWITCH_ID_LIST(INDEX, LIST) \ + if (idx == (INDEX)) { \ err = ChunkAssignDataImageInfo(&chunk, data, size, \ image_info, \ - copy_data, kChunks[(ID)].tag); \ + copy_data, kChunks[(INDEX)].tag); \ if (err == WEBP_MUX_OK) { \ err = ChunkSetNth(&chunk, (LIST), nth); \ } \ return err; \ } -static WebPMuxError MuxSet(WebPMux* const mux, TAG_ID id, uint32_t nth, +static WebPMuxError MuxSet(WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth, const uint8_t* data, size_t size, WebPImageInfo* image_info, int copy_data) { WebPChunk chunk; WebPMuxError err = WEBP_MUX_NOT_FOUND; assert(mux != NULL); - assert(!IsWPI(id)); + assert(!IsWPI(kChunks[idx].id)); ChunkInit(&chunk); - SWITCH_ID_LIST(VP8X_ID, &mux->vp8x_); - SWITCH_ID_LIST(ICCP_ID, &mux->iccp_); - SWITCH_ID_LIST(LOOP_ID, &mux->loop_); - SWITCH_ID_LIST(META_ID, &mux->meta_); - if (id == UNKNOWN_ID && size > TAG_SIZE) { + SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_); + SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_); + SWITCH_ID_LIST(IDX_LOOP, &mux->loop_); + SWITCH_ID_LIST(IDX_META, &mux->meta_); + if (idx == IDX_UNKNOWN && size > TAG_SIZE) { // For raw-data unknown chunk, the first four bytes should be the tag to be // used for the chunk. err = ChunkAssignDataImageInfo(&chunk, data + TAG_SIZE, size - TAG_SIZE, @@ -99,12 +99,12 @@ static WebPMuxError MuxSet(WebPMux* const mux, TAG_ID id, uint32_t nth, static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag, const uint8_t* data, size_t size, WebPImageInfo* image_info, int copy_data) { - const TAG_ID id = ChunkGetIdFromTag(tag); + const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag); assert(mux != NULL); assert(size <= MAX_CHUNK_PAYLOAD); - if (id == NIL_ID) return WEBP_MUX_INVALID_PARAMETER; - return MuxSet(mux, id, nth, data, size, image_info, copy_data); + if (idx == IDX_NIL) return WEBP_MUX_INVALID_PARAMETER; + return MuxSet(mux, idx, nth, data, size, image_info, copy_data); } static void InitImageInfo(WebPImageInfo* const image_info) { @@ -113,17 +113,19 @@ static void InitImageInfo(WebPImageInfo* const image_info) { } // Creates WebPImageInfo object and sets offsets, dimensions and duration. -// Dimensions calculated from passed VP8 image data. +// Dimensions calculated from passed VP8/VP8L image data. static WebPImageInfo* CreateImageInfo(uint32_t x_offset, uint32_t y_offset, uint32_t duration, - const uint8_t* data, size_t size) { + const uint8_t* data, size_t size, + int is_lossless) { int width; int height; WebPImageInfo* image_info = NULL; - if (!VP8GetInfo(data, size, size, &width, &height)) { - return NULL; - } + const int ok = is_lossless ? + VP8LGetInfo(data, size, &width, &height) : + VP8GetInfo(data, size, size, &width, &height); + if (!ok) return NULL; image_info = (WebPImageInfo*)malloc(sizeof(WebPImageInfo)); if (image_info != NULL) { @@ -146,7 +148,7 @@ static WebPMuxError CreateDataFromImageInfo(const WebPImageInfo* image_info, assert(size); assert(image_info); - *size = kChunks[is_frame ? FRAME_ID : TILE_ID].size; + *size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size; *data = (uint8_t*)malloc(*size); if (*data == NULL) return WEBP_MUX_MEMORY_ERROR; @@ -162,13 +164,20 @@ static WebPMuxError CreateDataFromImageInfo(const WebPImageInfo* image_info, return WEBP_MUX_OK; } +static int IsLosslessData(const WebPData* const image) { + return (image->size_ >= 1 && image->bytes_[0] == LOSSLESS_MAGIC_BYTE); +} + // Outputs image data given data from a webp file (including RIFF header). +// Also outputs 'is_lossless' to be true if the given bitstream is lossless. static WebPMuxError GetImageData(const uint8_t* data, size_t size, - WebPData* const image, WebPData* const alpha) { + WebPData* const image, WebPData* const alpha, + int* const is_lossless) { if (size < TAG_SIZE || memcmp(data, "RIFF", TAG_SIZE)) { // It is NOT webp file data. Return input data as is. image->bytes_ = data; image->size_ = size; + *is_lossless = IsLosslessData(image); return WEBP_MUX_OK; } else { // It is webp file data. Extract image data from it. @@ -178,9 +187,9 @@ static WebPMuxError GetImageData(const uint8_t* data, size_t size, if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) { return WEBP_MUX_BAD_DATA; } - err = WebPMuxGetImage(mux, image, alpha); WebPMuxDelete(mux); + *is_lossless = IsLosslessData(image); return err; } } @@ -201,21 +210,22 @@ static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) { } static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, - const char* const tag) { - const TAG_ID id = ChunkGetIdFromName(tag); + const char* const name) { + const CHUNK_INDEX idx = ChunkGetIndexFromName(name); + const TAG_ID id = kChunks[idx].id; WebPChunk** chunk_list; - if (mux == NULL || tag == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (mux == NULL || name == NULL) return WEBP_MUX_INVALID_ARGUMENT; if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT; chunk_list = GetChunkListFromId(mux, id); if (chunk_list == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return DeleteChunks(chunk_list, kChunks[id].tag); + return DeleteChunks(chunk_list, kChunks[idx].tag); } static WebPMuxError DeleteLoopCount(WebPMux* const mux) { - return MuxDeleteAllNamedData(mux, kChunks[LOOP_ID].name); + return MuxDeleteAllNamedData(mux, kChunks[IDX_LOOP].name); } //------------------------------------------------------------------------------ @@ -230,14 +240,18 @@ WebPMuxError WebPMuxSetImage(WebPMux* const mux, WebPMuxImage wpi; WebPData image; const int has_alpha = (alpha_data != NULL && alpha_size != 0); + int is_lossless; + int image_tag; if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } - // If given data is for a whole webp file, extract only the VP8 data from it. - err = GetImageData(data, size, &image, NULL); + // If given data is for a whole webp file, + // extract only the VP8/VP8L data from it. + err = GetImageData(data, size, &image, NULL, &is_lossless); if (err != WEBP_MUX_OK) return err; + image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; // Delete the existing images. MuxImageDeleteAll(&mux->images_); @@ -247,7 +261,7 @@ WebPMuxError WebPMuxSetImage(WebPMux* const mux, if (has_alpha) { // Add alpha chunk. ChunkInit(&chunk); err = ChunkAssignDataImageInfo(&chunk, alpha_data, alpha_size, NULL, - copy_data, kChunks[ALPHA_ID].tag); + copy_data, kChunks[IDX_ALPHA].tag); if (err != WEBP_MUX_OK) return err; err = ChunkSetNth(&chunk, &wpi.alpha_, 1); if (err != WEBP_MUX_OK) return err; @@ -256,9 +270,9 @@ WebPMuxError WebPMuxSetImage(WebPMux* const mux, // Add image chunk. ChunkInit(&chunk); err = ChunkAssignDataImageInfo(&chunk, image.bytes_, image.size_, NULL, - copy_data, kChunks[IMAGE_ID].tag); + copy_data, image_tag); if (err != WEBP_MUX_OK) return err; - err = ChunkSetNth(&chunk, &wpi.vp8_, 1); + err = ChunkSetNth(&chunk, &wpi.img_, 1); if (err != WEBP_MUX_OK) return err; // Add this image to mux. @@ -279,7 +293,7 @@ WebPMuxError WebPMuxSetMetadata(WebPMux* const mux, if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given metadata chunk. - return MuxSet(mux, META_ID, 1, data, size, NULL, copy_data); + return MuxSet(mux, IDX_META, 1, data, size, NULL, copy_data); } WebPMuxError WebPMuxSetColorProfile(WebPMux* const mux, @@ -296,7 +310,7 @@ WebPMuxError WebPMuxSetColorProfile(WebPMux* const mux, if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given ICCP chunk. - return MuxSet(mux, ICCP_ID, 1, data, size, NULL, copy_data); + return MuxSet(mux, IDX_ICCP, 1, data, size, NULL, copy_data); } WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { @@ -310,12 +324,12 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given loop count. - data = (uint8_t*)malloc(kChunks[LOOP_ID].size); + data = (uint8_t*)malloc(kChunks[IDX_LOOP].size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE32(data, loop_count); - err = MuxAddChunk(mux, 1, kChunks[LOOP_ID].tag, data, - kChunks[LOOP_ID].size, NULL, 1); + err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, + kChunks[IDX_LOOP].size, NULL, 1); free(data); return err; } @@ -333,16 +347,20 @@ static WebPMuxError MuxAddFrameTileInternal( WebPImageInfo* image_info = NULL; uint8_t* frame_tile_data = NULL; size_t frame_tile_data_size = 0; - const int is_frame = (tag == kChunks[FRAME_ID].tag) ? 1 : 0; + const int is_frame = (tag == kChunks[IDX_FRAME].tag) ? 1 : 0; const int has_alpha = (alpha_data != NULL && alpha_size != 0); + int is_lossless; + int image_tag; if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } - // If given data is for a whole webp file, extract only the VP8 data from it. - err = GetImageData(data, size, &image, NULL); + // If given data is for a whole webp file, + // extract only the VP8/VP8L data from it. + err = GetImageData(data, size, &image, NULL, &is_lossless); if (err != WEBP_MUX_OK) return err; + image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; ChunkInit(&chunk); MuxImageInit(&wpi); @@ -350,7 +368,7 @@ static WebPMuxError MuxAddFrameTileInternal( if (has_alpha) { // Add alpha chunk. err = ChunkAssignDataImageInfo(&chunk, alpha_data, alpha_size, NULL, - copy_data, kChunks[ALPHA_ID].tag); + copy_data, kChunks[IDX_ALPHA].tag); if (err != WEBP_MUX_OK) return err; err = ChunkSetNth(&chunk, &wpi.alpha_, 1); if (err != WEBP_MUX_OK) return err; @@ -359,7 +377,7 @@ static WebPMuxError MuxAddFrameTileInternal( // Create image_info object. image_info = CreateImageInfo(x_offset, y_offset, duration, - image.bytes_, image.size_); + image.bytes_, image.size_, is_lossless); if (image_info == NULL) { MuxImageRelease(&wpi); return WEBP_MUX_MEMORY_ERROR; @@ -367,15 +385,15 @@ static WebPMuxError MuxAddFrameTileInternal( // Add image chunk. err = ChunkAssignDataImageInfo(&chunk, image.bytes_, image.size_, image_info, - copy_data, kChunks[IMAGE_ID].tag); + copy_data, image_tag); if (err != WEBP_MUX_OK) goto Err; image_info = NULL; // Owned by 'chunk' now. - err = ChunkSetNth(&chunk, &wpi.vp8_, 1); + err = ChunkSetNth(&chunk, &wpi.img_, 1); if (err != WEBP_MUX_OK) goto Err; ChunkInit(&chunk); // chunk owned by wpi.vp8_ now. // Create frame/tile data from image_info. - err = CreateDataFromImageInfo(wpi.vp8_->image_info_, is_frame, + err = CreateDataFromImageInfo(wpi.img_->image_info_, is_frame, &frame_tile_data, &frame_tile_data_size); if (err != WEBP_MUX_OK) goto Err; @@ -413,7 +431,7 @@ WebPMuxError WebPMuxAddFrame(WebPMux* const mux, uint32_t nth, uint32_t duration, int copy_data) { return MuxAddFrameTileInternal(mux, nth, data, size, alpha_data, alpha_size, x_offset, y_offset, duration, - copy_data, kChunks[FRAME_ID].tag); + copy_data, kChunks[IDX_FRAME].tag); } WebPMuxError WebPMuxAddTile(WebPMux* const mux, uint32_t nth, @@ -423,7 +441,7 @@ WebPMuxError WebPMuxAddTile(WebPMux* const mux, uint32_t nth, int copy_data) { return MuxAddFrameTileInternal(mux, nth, data, size, alpha_data, alpha_size, x_offset, y_offset, 1, - copy_data, kChunks[TILE_ID].tag); + copy_data, kChunks[IDX_TILE].tag); } //------------------------------------------------------------------------------ @@ -443,35 +461,56 @@ WebPMuxError WebPMuxDeleteImage(WebPMux* const mux) { } WebPMuxError WebPMuxDeleteMetadata(WebPMux* const mux) { - return MuxDeleteAllNamedData(mux, kChunks[META_ID].name); + return MuxDeleteAllNamedData(mux, kChunks[IDX_META].name); } WebPMuxError WebPMuxDeleteColorProfile(WebPMux* const mux) { - return MuxDeleteAllNamedData(mux, kChunks[ICCP_ID].name); + return MuxDeleteAllNamedData(mux, kChunks[IDX_ICCP].name); } static WebPMuxError DeleteFrameTileInternal(WebPMux* const mux, uint32_t nth, - const char* const tag) { - const TAG_ID id = ChunkGetIdFromName(tag); + const char* const name) { + const CHUNK_INDEX idx = ChunkGetIndexFromName(name); + const TAG_ID id = kChunks[idx].id; if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - assert(id == FRAME_ID || id == TILE_ID); + assert(idx == IDX_FRAME || idx == IDX_TILE); return MuxImageDeleteNth(&mux->images_, nth, id); } WebPMuxError WebPMuxDeleteFrame(WebPMux* const mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, kChunks[FRAME_ID].name); + return DeleteFrameTileInternal(mux, nth, kChunks[IDX_FRAME].name); } WebPMuxError WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, kChunks[TILE_ID].name); + return DeleteFrameTileInternal(mux, nth, kChunks[IDX_TILE].name); } //------------------------------------------------------------------------------ // Assembly of the WebP RIFF file. -static WebPMuxError GetImageCanvasHeightWidth( +static WebPMuxError GetImageWidthHeight(const WebPChunk* const image_chunk, + int* const width, int* const height) { + const uint32_t tag = image_chunk->tag_; + int w, h; + int ok; + assert(image_chunk != NULL); + assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag); + ok = (tag == kChunks[IDX_VP8].tag) ? + VP8GetInfo(image_chunk->data_, image_chunk->payload_size_, + image_chunk->payload_size_, &w, &h) : + VP8LGetInfo(image_chunk->data_, image_chunk->payload_size_, &w, &h); + if (ok) { + *width = w; + *height = h; + return WEBP_MUX_OK; + } else { + return WEBP_MUX_BAD_DATA; + } +} + +static WebPMuxError GetImageCanvasWidthHeight( const WebPMux* const mux, uint32_t flags, uint32_t* width, uint32_t* height) { WebPMuxImage* wpi = NULL; @@ -480,7 +519,7 @@ static WebPMuxError GetImageCanvasHeightWidth( wpi = mux->images_; assert(wpi != NULL); - assert(wpi->vp8_ != NULL); + assert(wpi->img_ != NULL); if (wpi->next_) { uint32_t max_x = 0; @@ -488,7 +527,7 @@ static WebPMuxError GetImageCanvasHeightWidth( uint64_t image_area = 0; // Aggregate the bounding box for animation frames & tiled images. for (; wpi != NULL; wpi = wpi->next_) { - const WebPImageInfo* image_info = wpi->vp8_->image_info_; + const WebPImageInfo* image_info = wpi->img_->image_info_; if (image_info != NULL) { const uint32_t max_x_pos = image_info->x_offset_ + image_info->width_; @@ -516,15 +555,13 @@ static WebPMuxError GetImageCanvasHeightWidth( return WEBP_MUX_INVALID_ARGUMENT; } } else { - // For a single image, extract the width & height from VP8 image-data. + // For a single image, extract the width & height from VP8/VP8L image-data. int w, h; - const WebPChunk* const image_chunk = wpi->vp8_; - assert(image_chunk != NULL); - if (VP8GetInfo(image_chunk->data_, image_chunk->payload_size_, - image_chunk->payload_size_, &w, &h)) { - *width = w; - *height = h; - } + const WebPChunk* const image_chunk = wpi->img_; + const WebPMuxError err = GetImageWidthHeight(image_chunk, &w, &h); + if (err != WEBP_MUX_OK) return err; + *width = w; + *height = h; } return WEBP_MUX_OK; } @@ -545,13 +582,13 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { assert(mux != NULL); images = mux->images_; // First image. - if (images == NULL || images->vp8_ == NULL || images->vp8_->data_ == NULL) { + if (images == NULL || images->img_ == NULL || images->img_->data_ == 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[VP8X_ID].name); + err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].name); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Set flags. @@ -564,10 +601,10 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { } if (images->header_ != NULL) { - if (images->header_->tag_ == kChunks[TILE_ID].tag) { + if (images->header_->tag_ == kChunks[IDX_TILE].tag) { // This is a tiled image. flags |= TILE_FLAG; - } else if (images->header_->tag_ == kChunks[FRAME_ID].tag) { + } else if (images->header_->tag_ == kChunks[IDX_FRAME].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } @@ -583,14 +620,14 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { return WEBP_MUX_OK; } - err = GetImageCanvasHeightWidth(mux, flags, &width, &height); + err = GetImageCanvasWidthHeight(mux, flags, &width, &height); if (err != WEBP_MUX_OK) return err; PutLE32(data + 0, flags); // VP8X chunk flags. PutLE32(data + 4, width); // canvas width. PutLE32(data + 8, height); // canvas height. - err = MuxAddChunk(mux, 1, kChunks[VP8X_ID].tag, data, data_size, + err = MuxAddChunk(mux, 1, kChunks[IDX_VP8X].tag, data, data_size, NULL, 1); return err; } @@ -612,11 +649,11 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, *output_size = 0; // Remove LOOP chunk if unnecessary. - err = WebPMuxNumNamedElements(mux, kChunks[LOOP_ID].name, + err = WebPMuxNumNamedElements(mux, kChunks[IDX_LOOP].name, &num_loop_chunks); if (err != WEBP_MUX_OK) return err; if (num_loop_chunks >= 1) { - err = WebPMuxNumNamedElements(mux, kChunks[FRAME_ID].name, + err = WebPMuxNumNamedElements(mux, kChunks[IDX_FRAME].name, &num_frames); if (err != WEBP_MUX_OK) return err; if (num_frames == 0) { diff --git a/src/mux/muxi.h b/src/mux/muxi.h index 4a52c480..844788fa 100644 --- a/src/mux/muxi.h +++ b/src/mux/muxi.h @@ -14,6 +14,7 @@ #include #include "../dec/vp8i.h" +#include "../dec/vp8li.h" #include "../dec/webpi.h" // For chunk-size constants. #include "../webp/mux.h" @@ -47,12 +48,12 @@ struct WebPChunk { }; // MuxImage object. Store a full webp image (including frame/tile chunk, alpha -// chunk and VP8 chunk), +// chunk and VP8/VP8L chunk), typedef struct WebPMuxImage WebPMuxImage; struct WebPMuxImage { WebPChunk* header_; // Corresponds to FRAME_ID/TILE_ID. WebPChunk* alpha_; // Corresponds to ALPHA_ID. - WebPChunk* vp8_; // Corresponds to IMAGE_ID. + WebPChunk* img_; // Corresponds to IMAGE_ID. int is_partial_; // True if only some of the chunks are filled. WebPMuxImage* next_; }; @@ -69,8 +70,9 @@ struct WebPMux { WebPChunk* unknown_; }; +// TAG_ID enum: used to assign an ID to each type of chunk. typedef enum { - VP8X_ID = 0, + VP8X_ID, ICCP_ID, LOOP_ID, FRAME_ID, @@ -79,11 +81,29 @@ typedef enum { IMAGE_ID, META_ID, UNKNOWN_ID, - - NIL_ID, - LAST_TAG_ID + NIL_ID } TAG_ID; +// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only. +// Note: the reason for having two enums ('TAG_ID' and 'CHUNK_INDEX') is to +// allow two different chunks to have the same id (e.g. TAG_ID 'IMAGE_ID' can +// correspond to CHUNK_INDEX 'IDX_VP8' or 'IDX_VP8L'). +typedef enum { + IDX_VP8X = 0, + IDX_ICCP, + IDX_LOOP, + IDX_FRAME, + IDX_TILE, + IDX_ALPHA, + IDX_VP8, + IDX_VP8L, + IDX_META, + IDX_UNKNOWN, + + IDX_NIL, + IDX_LAST_CHUNK +} CHUNK_INDEX; + // Maximum chunk payload (data) size such that adding the header and padding // won't overflow an uint32. #define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1) @@ -101,7 +121,7 @@ typedef struct { uint32_t size; } ChunkInfo; -extern const ChunkInfo kChunks[LAST_TAG_ID]; +extern const ChunkInfo kChunks[IDX_LAST_CHUNK]; //------------------------------------------------------------------------------ // Helper functions. @@ -130,10 +150,16 @@ static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) { // Initialize. void ChunkInit(WebPChunk* const chunk); -// Get chunk id from chunk name. -TAG_ID ChunkGetIdFromName(const char* const what); +// Get chunk index from chunk name. +// Returns IDX_NIL if chunk name is NULL or not found. +CHUNK_INDEX ChunkGetIndexFromName(const char* const name); + +// Get chunk index from chunk tag. +// Returns IDX_NIL if not found. +CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag); // Get chunk id from chunk tag. +// Returns NIL_ID if not found. TAG_ID ChunkGetIdFromTag(uint32_t tag); // Search for nth chunk with given 'tag' in the chunk list. @@ -185,8 +211,7 @@ WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi); // Delete all images in 'wpi_list'. void MuxImageDeleteAll(WebPMuxImage** const wpi_list); -// Count number of images matching 'tag' in the 'wpi_list'. -// If tag == NIL_TAG, any tag will be matched. +// Count number of images matching the given tag id in the 'wpi_list'. int MuxImageCount(WebPMuxImage* const wpi_list, TAG_ID id); // Check if given ID corresponds to an image related chunk. @@ -206,9 +231,9 @@ static WEBP_INLINE WebPChunk** MuxImageGetListFromId(WebPMuxImage* wpi, assert(wpi != NULL); switch (id) { case FRAME_ID: - case TILE_ID: return &wpi->header_; + case TILE_ID: return &wpi->header_; case ALPHA_ID: return &wpi->alpha_; - case IMAGE_ID: return &wpi->vp8_; + case IMAGE_ID: return &wpi->img_; default: return NULL; } } @@ -218,11 +243,11 @@ static WEBP_INLINE WebPChunk** MuxImageGetListFromId(WebPMuxImage* wpi, WebPMuxError MuxImageSetNth(const WebPMuxImage* wpi, WebPMuxImage** wpi_list, uint32_t nth); -// Delete nth image in the image list with given tag. +// Delete nth image in the image list with given tag id. WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth, TAG_ID id); -// Get nth image in the image list with given tag. +// Get nth image in the image list with given tag id. WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, TAG_ID id, WebPMuxImage** wpi); diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 6217001e..7c55e2ac 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -27,6 +27,7 @@ const ChunkInfo kChunks[] = { {"tile", mktag('T', 'I', 'L', 'E'), TILE_ID, TILE_CHUNK_SIZE}, {"alpha", mktag('A', 'L', 'P', 'H'), ALPHA_ID, UNDEFINED_CHUNK_SIZE}, {"image", mktag('V', 'P', '8', ' '), IMAGE_ID, UNDEFINED_CHUNK_SIZE}, + {"image", mktag('V', 'P', '8', 'L'), IMAGE_ID, UNDEFINED_CHUNK_SIZE}, {"meta", mktag('M', 'E', 'T', 'A'), META_ID, UNDEFINED_CHUNK_SIZE}, {"unknown", mktag('U', 'N', 'K', 'N'), UNKNOWN_ID, UNDEFINED_CHUNK_SIZE}, @@ -57,19 +58,27 @@ WebPChunk* ChunkRelease(WebPChunk* const chunk) { //------------------------------------------------------------------------------ // Chunk misc methods. -TAG_ID ChunkGetIdFromName(const char* const what) { +CHUNK_INDEX ChunkGetIndexFromName(const char* const name) { int i; - if (what == NULL) return -1; + if (name == NULL) return IDX_NIL; for (i = 0; kChunks[i].name != NULL; ++i) { - if (!strcmp(what, kChunks[i].name)) return i; + if (!strcmp(name, kChunks[i].name)) return i; } - return NIL_ID; + return IDX_NIL; +} + +CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) { + int i; + for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { + if (tag == kChunks[i].tag) return i; + } + return IDX_NIL; } TAG_ID ChunkGetIdFromTag(uint32_t tag) { int i; for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { - if (tag == kChunks[i].tag) return i; + if (tag == kChunks[i].tag) return kChunks[i].id; } return NIL_ID; } @@ -127,7 +136,7 @@ WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk, WebPImageInfo* image_info, int copy_data, uint32_t tag) { // For internally allocated chunks, always copy data & make it owner of data. - if (tag == kChunks[VP8X_ID].tag || tag == kChunks[LOOP_ID].tag) { + if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_LOOP].tag) { copy_data = 1; } @@ -155,7 +164,7 @@ WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk, } } - if (tag == kChunks[IMAGE_ID].tag) { + if (tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag) { chunk->image_info_ = image_info; } @@ -234,7 +243,7 @@ WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) { if (wpi == NULL) return NULL; ChunkDelete(wpi->header_); ChunkDelete(wpi->alpha_); - ChunkDelete(wpi->vp8_); + ChunkDelete(wpi->img_); next = wpi->next_; MuxImageInit(wpi); @@ -249,8 +258,9 @@ int MuxImageCount(WebPMuxImage* const wpi_list, TAG_ID id) { WebPMuxImage* current; for (current = wpi_list; current != NULL; current = current->next_) { const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(current, id); - if (wpi_chunk != NULL && wpi_chunk->tag_ == kChunks[id].tag) { - ++count; + if (wpi_chunk != NULL) { + const TAG_ID wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); + if (wpi_chunk_id == id) ++count; } } return count; @@ -298,9 +308,12 @@ static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth, while (*wpi_list) { WebPMuxImage* const cur_wpi = *wpi_list; const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(cur_wpi, id); - if (wpi_chunk != NULL && wpi_chunk->tag_ == kChunks[id].tag) { - ++count; - if (count == nth) return 1; // Found. + if (wpi_chunk != NULL) { + const TAG_ID wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); + if (wpi_chunk_id == id) { + ++count; + if (count == nth) return 1; // Found. + } } wpi_list = &cur_wpi->next_; *location = wpi_list; @@ -376,7 +389,7 @@ static size_t MuxImageDiskSize(const WebPMuxImage* wpi) { size_t size = 0; if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_); if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_); - if (wpi->vp8_ != NULL) size += ChunkDiskSize(wpi->vp8_); + if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_); return size; } @@ -393,11 +406,11 @@ static uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { // Ordering of chunks to be emitted is strictly as follows: // 1. Frame/Tile chunk (if present). // 2. Alpha chunk (if present). - // 3. VP8 chunk. + // 3. VP8/VP8L chunk. assert(wpi); if (wpi->header_ != NULL) dst = ChunkEmit(wpi->header_, dst); if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst); - if (wpi->vp8_ != NULL) dst = ChunkEmit(wpi->vp8_, dst); + if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst); return dst; } @@ -415,24 +428,24 @@ uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) { WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id) { assert(mux != NULL); switch(id) { - case VP8X_ID: return (WebPChunk**)&mux->vp8x_; - case ICCP_ID: return (WebPChunk**)&mux->iccp_; - case LOOP_ID: return (WebPChunk**)&mux->loop_; - case META_ID: return (WebPChunk**)&mux->meta_; + case VP8X_ID: return (WebPChunk**)&mux->vp8x_; + case ICCP_ID: return (WebPChunk**)&mux->iccp_; + case LOOP_ID: return (WebPChunk**)&mux->loop_; + case META_ID: return (WebPChunk**)&mux->meta_; case UNKNOWN_ID: return (WebPChunk**)&mux->unknown_; default: return NULL; } } WebPMuxError ValidateForImage(const WebPMux* const mux) { - const int num_vp8 = MuxImageCount(mux->images_, IMAGE_ID); + const int num_images = MuxImageCount(mux->images_, IMAGE_ID); const int num_frames = MuxImageCount(mux->images_, FRAME_ID); - const int num_tiles = MuxImageCount(mux->images_, TILE_ID); + const int num_tiles = MuxImageCount(mux->images_, TILE_ID); - if (num_vp8 == 0) { + if (num_images == 0) { // No images in mux. return WEBP_MUX_NOT_FOUND; - } else if (num_vp8 == 1 && num_frames == 0 && num_tiles == 0) { + } else if (num_images == 1 && num_frames == 0 && num_tiles == 0) { // Valid case (single image). return WEBP_MUX_OK; } else { @@ -448,16 +461,14 @@ static int IsNotCompatible(int feature, int num_items) { #define NO_FLAG 0 // Test basic constraints: -// retrieval, maximum number of chunks by id (use -1 to skip) +// 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, TAG_ID id, +static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, FeatureFlags feature, FeatureFlags vp8x_flags, int max, int* num) { const WebPMuxError err = - WebPMuxNumNamedElements(mux, kChunks[id].name, num); - assert(id == kChunks[id].id); - + WebPMuxNumNamedElements(mux, kChunks[idx].name, 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)) { @@ -493,18 +504,18 @@ WebPMuxError WebPMuxValidate(const WebPMux* const mux) { if (err != WEBP_MUX_OK) return err; // At most one color profile chunk. - err = ValidateChunk(mux, ICCP_ID, ICCP_FLAG, flags, 1, &num_iccp); + err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp); if (err != WEBP_MUX_OK) return err; // At most one XMP metadata. - err = ValidateChunk(mux, META_ID, META_FLAG, flags, 1, &num_meta); + err = ValidateChunk(mux, IDX_META, META_FLAG, flags, 1, &num_meta); if (err != WEBP_MUX_OK) return err; // Animation: ANIMATION_FLAG, loop chunk and frame chunk(s) are consistent. // At most one loop chunk. - err = ValidateChunk(mux, LOOP_ID, NO_FLAG, flags, 1, &num_loop_chunks); + err = ValidateChunk(mux, IDX_LOOP, NO_FLAG, flags, 1, &num_loop_chunks); if (err != WEBP_MUX_OK) return err; - err = ValidateChunk(mux, FRAME_ID, NO_FLAG, flags, -1, &num_frames); + err = ValidateChunk(mux, IDX_FRAME, NO_FLAG, flags, -1, &num_frames); if (err != WEBP_MUX_OK) return err; { @@ -518,19 +529,19 @@ WebPMuxError WebPMuxValidate(const WebPMux* const mux) { } // Tiling: TILE_FLAG and tile chunk(s) are consistent. - err = ValidateChunk(mux, TILE_ID, TILE_FLAG, flags, -1, &num_tiles); + err = ValidateChunk(mux, IDX_TILE, TILE_FLAG, flags, -1, &num_tiles); 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, VP8X_ID, NO_FLAG, flags, 1, &num_vp8x); + err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x); if (err != WEBP_MUX_OK) return err; - err = ValidateChunk(mux, IMAGE_ID, NO_FLAG, flags, -1, &num_images); + 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. - err = ValidateChunk(mux, ALPHA_ID, ALPHA_FLAG, flags, -1, &num_alpha); + err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha); if (err != WEBP_MUX_OK) return err; // num_images & num_alpha_chunks are consistent. diff --git a/src/mux/muxread.c b/src/mux/muxread.c index d62fb300..23163213 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -21,10 +21,10 @@ extern "C" { // Helper method(s). // Handy MACRO. -#define SWITCH_ID_LIST(ID, LIST) \ - if (id == (ID)) { \ +#define SWITCH_ID_LIST(INDEX, LIST) \ + if (idx == (INDEX)) { \ const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ - kChunks[(ID)].tag); \ + kChunks[(INDEX)].tag); \ if (chunk) { \ data->bytes_ = chunk->data_; \ data->size_ = chunk->payload_size_; \ @@ -34,17 +34,17 @@ extern "C" { } \ } -static WebPMuxError MuxGet(const WebPMux* const mux, TAG_ID id, uint32_t nth, - WebPData* const data) { +static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, + uint32_t nth, WebPData* const data) { assert(mux != NULL); - assert(!IsWPI(id)); + assert(!IsWPI(kChunks[idx].id)); memset(data, 0, sizeof(*data)); - SWITCH_ID_LIST(VP8X_ID, mux->vp8x_); - SWITCH_ID_LIST(ICCP_ID, mux->iccp_); - SWITCH_ID_LIST(LOOP_ID, mux->loop_); - SWITCH_ID_LIST(META_ID, mux->meta_); - SWITCH_ID_LIST(UNKNOWN_ID, mux->unknown_); + SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_); + SWITCH_ID_LIST(IDX_ICCP, mux->iccp_); + SWITCH_ID_LIST(IDX_LOOP, mux->loop_); + SWITCH_ID_LIST(IDX_META, mux->meta_); + SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_); return WEBP_MUX_NOT_FOUND; } #undef SWITCH_ID_LIST @@ -102,9 +102,10 @@ WebPMux* WebPMuxCreateInternal(const uint8_t* data, size_t size, int copy_data, } tag = GetLE32(data + RIFF_HEADER_SIZE); - if (tag != kChunks[IMAGE_ID].tag && tag != kChunks[VP8X_ID].tag) { - // First chunk should be either VP8X or VP8. - goto Err; + if (tag != kChunks[IDX_VP8].tag && + tag != kChunks[IDX_VP8L].tag && + tag != kChunks[IDX_VP8X].tag) { + goto Err; // First chunk should be VP8, VP8L or VP8X. } riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); @@ -203,9 +204,9 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) { *flags = 0; // Check if VP8X chunk is present. - err = MuxGet(mux, VP8X_ID, 1, &data); + err = MuxGet(mux, IDX_VP8X, 1, &data); if (err == WEBP_MUX_NOT_FOUND) { - // Check if VP8 chunk is present. + // Check if VP8/VP8L chunk is present. err = WebPMuxGetImage(mux, &data, NULL); if (err == WEBP_MUX_NOT_FOUND && // Data not available (yet). mux->state_ == WEBP_MUX_STATE_PARTIAL) { // Incremental case. @@ -252,9 +253,9 @@ WebPMuxError WebPMuxGetImage(const WebPMux* const mux, // Get image chunk. if (image != NULL) { memset(image, 0, sizeof(*image)); - if (wpi->vp8_ != NULL) { - image->bytes_ = wpi->vp8_->data_; - image->size_ = wpi->vp8_->payload_size_; + if (wpi->img_ != NULL) { + image->bytes_ = wpi->img_->data_; + image->size_ = wpi->img_->payload_size_; } } return WEBP_MUX_OK; @@ -263,13 +264,13 @@ WebPMuxError WebPMuxGetImage(const WebPMux* const mux, WebPMuxError WebPMuxGetMetadata(const WebPMux* const mux, WebPData* const metadata) { if (mux == NULL || metadata == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return MuxGet(mux, META_ID, 1, metadata); + return MuxGet(mux, IDX_META, 1, metadata); } WebPMuxError WebPMuxGetColorProfile(const WebPMux* const mux, WebPData* const color_profile) { if (mux == NULL || color_profile == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return MuxGet(mux, ICCP_ID, 1, color_profile); + return MuxGet(mux, IDX_ICCP, 1, color_profile); } WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, @@ -279,7 +280,7 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, if (mux == NULL || loop_count == NULL) return WEBP_MUX_INVALID_ARGUMENT; - err = MuxGet(mux, LOOP_ID, 1, &image); + err = MuxGet(mux, IDX_LOOP, 1, &image); if (err != WEBP_MUX_OK) return err; if (image.size_ < kChunks[LOOP_ID].size) return WEBP_MUX_BAD_DATA; *loop_count = GetLE32(image.bytes_); @@ -297,7 +298,8 @@ static WebPMuxError MuxGetFrameTileInternal( WebPMuxImage* wpi; const int is_frame = (tag == kChunks[FRAME_ID].tag) ? 1 : 0; - const TAG_ID id = is_frame ? FRAME_ID : TILE_ID; + const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE; + const TAG_ID id = kChunks[idx].id; if (mux == NULL || image == NULL || x_offset == NULL || y_offset == NULL || (is_frame && duration == NULL)) { @@ -313,7 +315,7 @@ static WebPMuxError MuxGetFrameTileInternal( frame_tile_data = wpi->header_->data_; frame_tile_size = wpi->header_->payload_size_; - if (frame_tile_size < kChunks[id].size) return WEBP_MUX_BAD_DATA; + if (frame_tile_size < kChunks[idx].size) return WEBP_MUX_BAD_DATA; *x_offset = GetLE32(frame_tile_data + 0); *y_offset = GetLE32(frame_tile_data + 4); if (is_frame) *duration = GetLE32(frame_tile_data + 16); @@ -329,9 +331,9 @@ static WebPMuxError MuxGetFrameTileInternal( // Get image chunk. memset(image, 0, sizeof(*image)); - if (wpi->vp8_ != NULL) { - image->bytes_ = wpi->vp8_->data_; - image->size_ = wpi->vp8_->payload_size_; + if (wpi->img_ != NULL) { + image->bytes_ = wpi->img_->data_; + image->size_ = wpi->img_->payload_size_; } return WEBP_MUX_OK; @@ -343,7 +345,7 @@ WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, uint32_t* duration) { return MuxGetFrameTileInternal(mux, nth, image, alpha, x_offset, y_offset, duration, - kChunks[FRAME_ID].tag); + kChunks[IDX_FRAME].tag); } WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, @@ -351,7 +353,7 @@ WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, uint32_t* x_offset, uint32_t* y_offset) { return MuxGetFrameTileInternal(mux, nth, image, alpha, x_offset, y_offset, NULL, - kChunks[TILE_ID].tag); + kChunks[IDX_TILE].tag); } // Count number of chunks matching 'tag' in the 'chunk_list'. @@ -367,11 +369,12 @@ static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) { return count; } -WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* tag, +WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* name, int* num_elements) { - const TAG_ID id = ChunkGetIdFromName(tag); + const CHUNK_INDEX idx = ChunkGetIndexFromName(name); + const TAG_ID id = kChunks[idx].id; - if (mux == NULL || tag == NULL || num_elements == NULL) { + if (mux == NULL || name == NULL || num_elements == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } @@ -382,7 +385,7 @@ WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* tag, if (chunk_list == NULL) { *num_elements = 0; } else { - *num_elements = CountChunks(*chunk_list, kChunks[id].tag); + *num_elements = CountChunks(*chunk_list, kChunks[idx].tag); } } diff --git a/src/webp/mux.h b/src/webp/mux.h index 76aa70e1..a5348abb 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -139,7 +139,7 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const uint8_t* data, size_t size, // will be removed. // Parameters: // mux - (in/out) object in which the image is to be set -// data - (in) the image data to be set. The data can be either a VP8 +// data - (in) the image data to be set. The data can be either a VP8/VP8L // bitstream or a single-image WebP file (non-animated & non-tiled) // size - (in) size of the image data // alpha_data - (in) the alpha data corresponding to the image (if present) @@ -267,8 +267,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* const mux); // Parameters: // mux - (in/out) object to which an animation frame is to be added // nth - (in) The position at which the frame is to be added. -// data - (in) the raw VP8 image data corresponding to frame image. The data -// can be either a VP8 bitstream or a single-image WebP file +// data - (in) the raw VP8/VP8L image data corresponding to frame image. The +// data can be either a VP8/VP8L bitstream or a single-image WebP file // (non-animated & non-tiled) // size - (in) size of frame chunk data // alpha_data - (in) the alpha data corresponding to frame image (if present) @@ -367,8 +367,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux, // Parameters: // mux - (in/out) object to which a tile is to be added // nth - (in) The position at which the tile is to be added. -// data - (in) the raw VP8 image data corresponding to tile image. The data -// can be either a VP8 bitstream or a single-image WebP file +// data - (in) the raw VP8/VP8L image data corresponding to tile image. The +// data can be either a VP8/VP8L bitstream or a single-image WebP file // (non-animated & non-tiled) // size - (in) size of tile chunk data // alpha_data - (in) the alpha data corresponding to tile image (if present) @@ -442,13 +442,15 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetFeatures(const WebPMux* const mux, // Gets number of chunks having tag value tag in the mux object. // Parameters: // mux - (in) object from which the info is to be fetched -// tag - (in) tag name specifying the type of chunk +// name - (in) chunk name specifying the type of chunk +// Can be any of: "vp8x", "iccp", "loop", "frame", "tile", "alpha", +// "image", "meta" or "unknown" // num_elements - (out) number of chunks corresponding to the specified tag // Returns: // WEBP_MUX_INVALID_ARGUMENT - if either mux, tag or num_elements is NULL // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxNumNamedElements(const WebPMux* const mux, - const char* tag, + const char* name, int* num_elements); // Assembles all chunks in WebP RIFF format and returns in output_data.