From c398f595360b462a533b1ec8090bb3a087002122 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Tue, 22 Nov 2011 14:40:41 +0530 Subject: [PATCH] MUX API Updates - Add alpha support in mux. - Remove WebPMuxAddNamedData() and WebPMuxGetNamedData() APIs. Add WebPMuxSetImage(), WebPmuxGetImage() and WebPMuxDeleteImage() APIs instead. - Refactor code using WebPImage struct. - Corresponding changes in webpmux binary. - WebPMuxSetImage()/AddFrame()/AddTile() can now work with data which starts from "RIFF...". This simplifies reading a single-image webp file and adding it as an image/frame/tile in mux. Change-Id: I7d98a6407dfe55c84a682ef7e46bc622f5a6f8d9 --- README.mux | 6 +- doc/webp-container-spec.txt | 75 +++- examples/webpmux.c | 70 +-- src/mux/mux.c | 867 +++++++++++++++++++++++++----------- src/webp/mux.h | 135 ++++-- 5 files changed, 779 insertions(+), 374 deletions(-) diff --git a/README.mux b/README.mux index ca11a348..b9473d8f 100644 --- a/README.mux +++ b/README.mux @@ -73,7 +73,8 @@ metadata. int copy_data = 0; WebPMux* mux = WebPMuxNew(); // ... (Prepare image data). - WebPMuxAddNamedData(mux, 1, "image", image_data, image_data_size, copy_data); + WebPMuxSetImage(mux, image_data, image_data_size, alpha_data, alpha_size, + copy_data); // ... (Prepare ICCP color profile data). WebPMuxSetColorProfile(mux, icc_data, icc_data_size, copy_data); // ... (Prepare XMP metadata). @@ -90,7 +91,8 @@ Example#2 (pseudo code): Get image & color profile data from a WebP file. int copy_data = 0; // ... (Read data from file). WebPMux* mux = WebPMuxCreate(data, data_size, copy_data); - WebPMuxGetNamedData(mux, "image", 1, &image_data, &image_data_size); + WebPMuxGetImage(mux, &image_data, &image_data_size, + &alpha_data, &alpha_size); // ... (Consume image_data; e.g. call WebPDecode() to decode the data). WebPMuxGetColorProfile(mux, &icc_data, &icc_data_size); // ... (Consume icc_data). diff --git a/doc/webp-container-spec.txt b/doc/webp-container-spec.txt index 7fdb9e0c..39a455cf 100644 --- a/doc/webp-container-spec.txt +++ b/doc/webp-container-spec.txt @@ -50,6 +50,9 @@ document describes additional support for: * **Animation.** An image may have pauses between frames, making it an animation. + * **Transparency.** An image may have transparency, i.e., an alpha channel for + each frame/tile. + Files not using these new features are backward compatible with the original format. Use of these features will produce files that are not compatible with older programs. @@ -215,6 +218,10 @@ multiple frames. Data for each frame consists of: present at the beginning of data for an image that's represented as multiple tile images. + * An optional "ALPH" chunk with alpha bitstream of the tile. It **MUST** be + present for an image containing transparency. It **MUST NOT** be present + in non-transparent images. + * A "VP8 " chunk with the bitstream of the tile. All chunks **MUST** be placed in the same order as listed above (except @@ -228,8 +235,8 @@ position, a decoder can choose to stop searching for it. The rule of ignoring late chunks should make programs that need to do a full search give the same results as the ones stopping early. -**Example:** An example layout of a non-animated, tiled image may look -as follows: +**Example:** An example layout of a non-animated, tiled image without +transparency may look as follows: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RIFF/WEBP @@ -246,17 +253,20 @@ RIFF/WEBP +- META (XMP metadata) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Example:** An example layout of an animated image may look as follows: +**Example:** An example layout of an animated image with transparency may look +as follows: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RIFF/WEBP +- VP8X (descriptions of features used) +- LOOP (animation control parameters) +- FRM (first animation frame parameters) ++- ALPH (alpha bitstream - first image frame) +- VP8 (bitstream - first image frame) +- FRM (second animation frame parameters) ++- ALPH (alpha bitstream - second image frame) +- VP8 (bitstream - second image frame) -+- META(XMP metadata) ++- META (XMP metadata) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -272,14 +282,20 @@ pseudocode: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assert not VP8X.flags.haveAnimation -canvas ← new black image of size VP8X.canvasWidth x VP8X.canvasHeight. +canvas ← new black image of size + VP8X.canvasWidth x VP8X.canvasHeight. tile_params.tileCanvasX = tile_params.tileCanvasY = 0 for chunk in data_for_all_frames: if chunk.tag == "TILE": assert No other TILE chunk after the last "VP8 " chunk + assert No ALPH chunk after the last "VP8 " chunk tile_params = chunk + if chunk.tag == "ALPH": + assert No other ALPH chunk after the last "VP8 " chunk + tile_params.alpha = alpha_data if chunk.tag == "VP8 ": - render image in chunk in canvas with top-left corner in (tile_params.tileCanvasX, tile_params.tileCanvasY) using the isometry in VP8X.flags.rotationAndSymmetry. + render image in chunk in canvas with top-left corner in + (tile_params.tileCanvasX, tile_params.tileCanvasY). tile_params.tileCanvasX = tile_params.tileCanvasY = 0 Ignore unknown chunks canvas contains the decoded canvas. @@ -290,7 +306,8 @@ pseudocode: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ assert VP8X.flags.haveAnimation -canvas ← new black image of size VP8X.canvasWidth x VP8X.canvasHeight. +canvas ← new black image of size + VP8X.canvasWidth x VP8X.canvasHeight. if LOOP.loopCount==0: LOOP.loopCount=∞ current_FRM ← nil @@ -298,15 +315,24 @@ for LOOP.loop = 0, ..., LOOP.loopCount-1 assert First chunk in data_for_all_frames is a FRM for chunk in data_for_all_frames: if chunk.tag == "FRM ": + assert No other FRM chunk after the last "VP8 " chunk + assert No ALPH chunk after the last "VP8 " chunk if current_FRM != nil: - Show the contents of canvas for current_FRM.frameDuration*10ms. + Show the contents of canvas for + current_FRM.frameDuration * 1ms. current_FRM = chunk + if chunk.tag == "ALPH": + assert No other ALPH chunk after the last "VP8 " chunk + tile_params.alpha = alpha_data if chunk.tag == "VP8 ": assert tile_params.tileCanvasX >= current_FRM.frameX assert tile_params.tileCanvasY >= current_FRM.frameY - assert tile_params.tileCanvasX + chunk.tileWidth >= current_FRM.frameX + current_FRM.frameWidth - assert tile_params.tileCanvasY + chunk.tileHeight >= current_FRM.frameX + current_FRM.frameHeight - render image in chunk in canvas with top-left corner in (tile_params.tileCanvasX, tile_params.tileCanvasY) using the isometry in VP8X.flags.rotationAndSymmetry. + assert tile_params.tileCanvasX + chunk.tileWidth >= + current_FRM.frameX + current_FRM.frameWidth + assert tile_params.tileCanvasY + chunk.tileHeight >= + current_FRM.frameX + current_FRM.frameHeight + render image in chunk in canvas with top-left corner in + (tile_params.tileCanvasX, tile_params.tileCanvasY). tile_params.tileCanvasX = tile_params.tileCanvasY = 0 Ignore unknown chunks canvas contains the decoded canvas. @@ -482,6 +508,31 @@ constraints: _tileCanvasY + tileHeight == canvasHeight_ **MUST** be true. +### ALPH Chunks (Alpha Bitstreams) + +This optional chunk contains encoded alpha data for a single tile. Either +**ALL or NONE** of the tiles must contain this chunk. + +The alpha channel can be encoded either losslessly or with lossy preprocessing +(quantization). After the optional preprocessing, the alpha values are encoded +with a lossless compression method like zlib. Work is in progress to improve the +compression gain further by exploring alternate compression methods and hence, +the bit-stream for the Alpha-chunk is still experimental and expected to change. + +The contents of such a chunk are as follows: + + * Byte 0: The _compression method_ used. Currently two methods are + supported: + + * 0 --> No compression + + * 1 --> Zlib compression. + + * Byte 1: _Reserved_. **Should** be 0. + + * Byte 2 onwards: _Encoded alpha bitstream_. + + ### ICCP Chunk (Color Profile) An optional "ICCP" chunk contains an ICC profile. There **SHOULD** be @@ -525,4 +576,4 @@ specification. Such chunks **MUST** be ignored, but preserved. Writers [vp8spec]: http://tools.ietf.org/html/draft-bankoski-vp8-bitstream [xmpspec]: http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf -[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf \ No newline at end of file +[metadata]: http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf diff --git a/examples/webpmux.c b/examples/webpmux.c index f4bc8597..74131ed1 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -191,8 +191,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { fprintf(stderr, "No.: x_offset y_offset duration\n"); for (i = 1; i <= nFrames; i++) { - err = WebPMuxGetFrame(mux, i, &data, &size, &x_offset, &y_offset, - &duration); + err = WebPMuxGetFrame(mux, i, &data, &size, NULL, NULL, + &x_offset, &y_offset, &duration); assert(err == WEBP_MUX_OK); fprintf(stderr, "%3d: %8d %8d %8d\n", i, x_offset, y_offset, duration); } @@ -209,7 +209,8 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { uint32_t x_offset, y_offset; fprintf(stderr, "No.: x_offset y_offset\n"); for (i = 1; i <= nTiles; i++) { - err = WebPMuxGetTile(mux, i, &data, &size, &x_offset, &y_offset); + err = WebPMuxGetTile(mux, i, &data, &size, NULL, NULL, + &x_offset, &y_offset); assert(err == WEBP_MUX_OK); fprintf(stderr, "%3d: %8d %8d\n", i, x_offset, y_offset); } @@ -350,45 +351,6 @@ static int ReadFile(const char* const filename, WebPMux** mux) { return 0; } -static int ReadImageData(const char* filename, int image_index, - const uint8_t** data_ptr, uint32_t* size_ptr) { - uint32_t size = 0; - void* data = NULL; - WebPMux* mux; - WebPMuxError err; - int ok = 1; - - if (!ReadData(filename, &data, &size)) return 0; - - mux = WebPMuxCreate((const uint8_t*)data, size, 1); - free(data); - if (mux == NULL) { - fprintf(stderr, "Failed to create mux object from file %s.\n", - filename); - return 0; - } - err = WebPMuxGetNamedData(mux, "image", image_index, (const uint8_t**)&data, - &size); - if (err == WEBP_MUX_OK) { - *size_ptr = size; - *data_ptr = (uint8_t*)malloc(*size_ptr); - if (*data_ptr != NULL) { - memcpy((void*)*data_ptr, data, (size_t)size); - } else { - err = WEBP_MUX_MEMORY_ERROR; - fprintf(stderr, "Failed to allocate %d bytes to extract image data from" - " file %s. Error: %d\n", size, filename, err); - ok = 0; - } - } else { - fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n", - filename, err); - ok = 0; - } - WebPMuxDelete(mux); - return ok; -} - static int WriteData(const char* filename, void* data, uint32_t size) { int ok = 0; FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout; @@ -770,13 +732,14 @@ static int GetFrameTile(const WebPMux* mux, } if (isFrame) { - err = WebPMuxGetFrame(mux, num, &data, &size, &x_offset, &y_offset, - &duration); + err = WebPMuxGetFrame(mux, num, &data, &size, NULL, NULL, + &x_offset, &y_offset, &duration); if (err != WEBP_MUX_OK) { ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet); } } else { - err = WebPMuxGetTile(mux, num, &data, &size, &x_offset, &y_offset); + err = WebPMuxGetTile(mux, num, &data, &size, NULL, NULL, + &x_offset, &y_offset); if (err != WEBP_MUX_OK) { ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet); } @@ -787,7 +750,7 @@ static int GetFrameTile(const WebPMux* mux, err = WEBP_MUX_MEMORY_ERROR; ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet); } - err = WebPMuxAddNamedData(mux_single, 0, "image", data, size, 1); + err = WebPMuxSetImage(mux_single, data, size, NULL, 0, 1); if (err != WEBP_MUX_OK) { ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err, ErrGet); @@ -870,8 +833,8 @@ static int Process(const WebPMuxConfig* config) { ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2); } } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) { - ok = ReadImageData(feature->args_[index].filename_, 1, - &data, &size); + ok = ReadData(feature->args_[index].filename_, + (void**)&data, &size); if (!ok) goto Err2; ok = ParseFrameArgs(feature->args_[index].params_, &x_offset, &y_offset, &duration); @@ -879,8 +842,8 @@ static int Process(const WebPMuxConfig* config) { free((void*)data); ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2); } - err = WebPMuxAddFrame(mux, 0, data, size, x_offset, y_offset, - duration, 1); + err = WebPMuxAddFrame(mux, 0, data, size, NULL, 0, + x_offset, y_offset, duration, 1); free((void*)data); if (err != WEBP_MUX_OK) { ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n", @@ -899,8 +862,8 @@ static int Process(const WebPMuxConfig* config) { WEBP_MUX_MEMORY_ERROR, Err2); } for (index = 0; index < feature->arg_count_; ++index) { - ok = ReadImageData(feature->args_[index].filename_, 1, - &data, &size); + ok = ReadData(feature->args_[index].filename_, + (void**)&data, &size); if (!ok) goto Err2; ok = ParseTileArgs(feature->args_[index].params_, &x_offset, &y_offset); @@ -908,7 +871,8 @@ static int Process(const WebPMuxConfig* config) { free((void*)data); ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2); } - err = WebPMuxAddTile(mux, 0, data, size, x_offset, y_offset, 1); + err = WebPMuxAddTile(mux, 0, data, size, NULL, 0, + x_offset, y_offset, 1); free((void*)data); if (err != WEBP_MUX_OK) { ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n", diff --git a/src/mux/mux.c b/src/mux/mux.c index 12f13034..a861caf0 100644 --- a/src/mux/mux.c +++ b/src/mux/mux.c @@ -42,17 +42,28 @@ struct WebPChunk { WebPChunk* next_; }; +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. + int is_partial_; // True if only some of the chunks are filled. + WebPMuxImage* next_; +}; + // Main mux object. Stores data chunks. struct WebPMux { - WebPChunk* images_; - WebPChunk* iccp_; - WebPChunk* meta_; - WebPChunk* loop_; - WebPChunk* vp8x_; + WebPMuxImage* images_; + WebPChunk* iccp_; + WebPChunk* meta_; + WebPChunk* loop_; + WebPChunk* vp8x_; WebPChunk* unknown_; }; +static WebPMuxError DeleteLoopCount(WebPMux* const mux); + //------------------------------------------------------------------------------ // Internal struct management. @@ -65,6 +76,7 @@ typedef enum { LOOP_ID, FRAME_ID, TILE_ID, + ALPHA_ID, IMAGE_ID, META_ID, UNKNOWN_ID, @@ -96,6 +108,7 @@ static const ChunkInfo kChunks[] = { {"loop", mktag('L', 'O', 'O', 'P'), LOOP_ID, LOOP_CHUNK_SIZE}, {"frame", mktag('F', 'R', 'M', ' '), FRAME_ID, FRAME_CHUNK_SIZE}, {"tile", mktag('T', 'I', 'L', 'E'), TILE_ID, TILE_CHUNK_SIZE}, + {"alpha", mktag('A', 'L', 'P', 'H'), ALPHA_ID, -1}, {"image", mktag('V', 'P', '8', ' '), IMAGE_ID, -1}, {"meta", mktag('M', 'E', 'T', 'A'), META_ID, -1}, {"unknown", mktag('U', 'N', 'K', 'N'), UNKNOWN_ID, -1}, @@ -121,15 +134,15 @@ static TAG_ID GetChunkIdFromTag(uint32_t tag) { return NIL_ID; } -static WebPChunk** GetListFromId(const WebPMux* mux, TAG_ID id) { +// Returns the list where chunk with given ID is to be inserted. +// Return value is NULL if this chunk should be inserted in mux->images_ list +// or if 'id' is not known. +static 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 FRAME_ID: return (WebPChunk**)&mux->images_; - case TILE_ID: return (WebPChunk**)&mux->images_; - case IMAGE_ID: return (WebPChunk**)&mux->images_; case META_ID: return (WebPChunk**)&mux->meta_; case UNKNOWN_ID: return (WebPChunk**)&mux->unknown_; default: return NULL; @@ -231,33 +244,6 @@ static WebPChunk* SearchChunkList(WebPChunk* first, uint32_t nth, return ((nth > 0) && (iter > 0)) ? NULL : first; } -// Outputs a pointer to 'prev_chunk->next_', -// where 'prev_chunk' is the pointer to the chunk at position (nth - 1). -// If tag == NIL_TAG, any chunk will be matched. -// Returns 1 if nth chunk was found, 0 otherwise. -static int SearchChunkToDelete(WebPChunk** chunk_list, uint32_t nth, - uint32_t tag, WebPChunk*** const location) { - uint32_t count = 0; - assert(chunk_list); - *location = chunk_list; - - if (nth == 0) { - nth = ListCountChunks(*chunk_list, tag); - if (nth == 0) return 0; // Not found. - } - - while (*chunk_list) { - WebPChunk* const cur_chunk = *chunk_list; - if ((tag == NIL_TAG) || (cur_chunk->tag_ == tag)) { - ++count; - if (count == nth) return 1; // Found. - } - chunk_list = &cur_chunk->next_; - *location = chunk_list; - } - return 0; // Not found. -} - // Outputs a pointer to 'prev_chunk->next_', // where 'prev_chunk' is the pointer to the chunk at position (nth - 1). // Returns 1 if nth chunk was found, 0 otherwise. @@ -309,17 +295,209 @@ static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) { return err; } -// Delete nth chunk in the chunk list with given tag. -// If tag == NIL_TAG, any chunk will be matched. -static WebPMuxError DeleteNthChunk(WebPChunk** chunk_list, uint32_t nth, - uint32_t tag) { - if (!SearchChunkToDelete(chunk_list, nth, tag, &chunk_list)) { +// Size of a chunk including header and padding. +static uint32_t ChunkDiskSize(const WebPChunk* chunk) { + assert(chunk->payload_size_ < MAX_CHUNK_PAYLOAD); + return CHUNK_HEADER_SIZE + ((chunk->payload_size_ + 1) & ~1U); +} + +//------------------------------------------------------------------------------ +// WebPMuxImage object management. + +static void InitImage(WebPMuxImage* const wpi) { + assert(wpi); + wpi->header_ = NULL; + wpi->alpha_ = NULL; + wpi->vp8_ = NULL; + wpi->is_partial_ = 0; + wpi->next_ = NULL; +} + +// Releases wpi and returns wpi->next_. +static WebPMuxImage* ReleaseImage(WebPMuxImage* const wpi) { + WebPMuxImage* next; + if (wpi == NULL) return NULL; + DeleteChunk(wpi->header_); + DeleteChunk(wpi->alpha_); + DeleteChunk(wpi->vp8_); + + next = wpi->next_; + InitImage(wpi); + return next; +} + +static WebPMuxImage* DeleteImage(WebPMuxImage* const wpi) { + WebPMuxImage* const next = ReleaseImage(wpi); + free(wpi); + return next; +} + +static void DeleteAllImages(WebPMuxImage** const wpi_list) { + while (*wpi_list) { + *wpi_list = DeleteImage(*wpi_list); + } +} + +// Check if given ID corresponds to an image related chunk. +static int IsWPI(TAG_ID id) { + switch(id) { + case FRAME_ID: + case TILE_ID: + case ALPHA_ID: + case IMAGE_ID: return 1; + default: return 0; + } +} + +static WebPChunk** GetImageListFromId(WebPMuxImage* wpi, TAG_ID id) { + assert(wpi != NULL); + switch(id) { + case FRAME_ID: + case TILE_ID: return &wpi->header_; + case ALPHA_ID: return &wpi->alpha_; + case IMAGE_ID: return &wpi->vp8_; + default: return NULL; + } +} + +static int ListCountImages(WebPMuxImage* const wpi_list, TAG_ID id) { + int count = 0; + WebPMuxImage* current; + for (current = wpi_list; current != NULL; current = current->next_) { + WebPChunk** const wpi_chunk_ptr = GetImageListFromId(current, id); + assert(wpi_chunk_ptr != NULL); + + if ((*wpi_chunk_ptr != NULL) && + ((*wpi_chunk_ptr)->tag_ == kChunks[id].chunkTag)) { + ++count; + } + } + return count; +} + +// This validates that given mux has a single image. +static WebPMuxError ValidateForImage(const WebPMux* const mux) { + int num_vp8; + int num_frames; + int num_tiles; + num_vp8 = ListCountImages(mux->images_, IMAGE_ID); + num_frames = ListCountImages(mux->images_, FRAME_ID); + num_tiles = ListCountImages(mux->images_, TILE_ID); + + if (num_vp8 == 0) { + // No images in mux. + return WEBP_MUX_NOT_FOUND; + } else if (num_vp8 == 1 && num_frames == 0 && num_tiles == 0) { + // Valid case (single image). + return WEBP_MUX_OK; + } else { + // Frame/Tile case OR an invalid mux. + return WEBP_MUX_INVALID_ARGUMENT; + } +} + +// Outputs a pointer to 'prev_wpi->next_', +// where 'prev_wpi' is the pointer to the image at position (nth - 1). +// Returns 1 if nth image was found, 0 otherwise. +static int SearchImageToSet(WebPMuxImage** wpi_list, uint32_t nth, + WebPMuxImage*** const location) { + uint32_t count = 0; + assert(wpi_list); + *location = wpi_list; + + while (*wpi_list) { + WebPMuxImage* const cur_wpi = *wpi_list; + ++count; + if (count == nth) return 1; // Found. + wpi_list = &cur_wpi->next_; + *location = wpi_list; + } + + // *chunk_list is ok to be NULL if adding at last location. + return (nth == 0 || (count == nth - 1)) ? 1 : 0; +} + +static WebPMuxError SetNthImage(const WebPMuxImage* wpi, + WebPMuxImage** wpi_list, uint32_t nth) { + WebPMuxImage* new_wpi; + + if (!SearchImageToSet(wpi_list, nth, &wpi_list)) { return WEBP_MUX_NOT_FOUND; } - *chunk_list = DeleteChunk(*chunk_list); + + new_wpi = (WebPMuxImage*)malloc(sizeof(*new_wpi)); + if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR; + *new_wpi = *wpi; + new_wpi->next_ = *wpi_list; + *wpi_list = new_wpi; return WEBP_MUX_OK; } +// Outputs a pointer to 'prev_wpi->next_', +// where 'prev_wpi' is the pointer to the image at position (nth - 1). +// Returns 1 if nth image with given id was found, 0 otherwise. +static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth, + TAG_ID id, WebPMuxImage*** const location) { + uint32_t count = 0; + assert(wpi_list); + *location = wpi_list; + + // Search makes sense only for the following. + assert(id == FRAME_ID || id == TILE_ID || id == IMAGE_ID); + assert(id != IMAGE_ID || nth == 1); + + if (nth == 0) { + nth = ListCountImages(*wpi_list, id); + if (nth == 0) return 0; // Not found. + } + + while (*wpi_list) { + WebPMuxImage* const cur_wpi = *wpi_list; + WebPChunk** const wpi_chunk_ptr = GetImageListFromId(cur_wpi, id); + assert(wpi_chunk_ptr != NULL); + if ((*wpi_chunk_ptr)->tag_ == kChunks[id].chunkTag) { + ++count; + if (count == nth) return 1; // Found. + } + wpi_list = &cur_wpi->next_; + *location = wpi_list; + } + return 0; // Not found. +} + +// Delete nth image in the image list with given tag. +static WebPMuxError DeleteNthImage(WebPMuxImage** wpi_list, uint32_t nth, + TAG_ID id) { + assert(wpi_list); + if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) { + return WEBP_MUX_NOT_FOUND; + } + *wpi_list = DeleteImage(*wpi_list); + return WEBP_MUX_OK; +} + +// Get nth image in the image list with given tag. +static WebPMuxError GetNthImage(const WebPMuxImage** wpi_list, uint32_t nth, + TAG_ID id, WebPMuxImage** wpi) { + assert(wpi_list); + assert(wpi); + if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id, + (WebPMuxImage*** const)&wpi_list)) { + return WEBP_MUX_NOT_FOUND; + } + *wpi = (WebPMuxImage*)*wpi_list; + return WEBP_MUX_OK; +} + +// Size of an image. +static uint32_t ImageDiskSize(const WebPMuxImage* wpi) { + uint32_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_); + return size; +} + //------------------------------------------------------------------------------ // Mux object management. @@ -337,10 +515,10 @@ WebPMux* WebPMuxNew(void) { static int WebPMuxRelease(WebPMux* const mux) { if (mux == NULL) return 0; + DeleteAllImages(&mux->images_); DeleteAllChunks(&mux->vp8x_); DeleteAllChunks(&mux->iccp_); DeleteAllChunks(&mux->loop_); - DeleteAllChunks(&mux->images_); DeleteAllChunks(&mux->meta_); DeleteAllChunks(&mux->unknown_); return 1; @@ -360,12 +538,6 @@ static uint32_t GetLE32(const uint8_t* const data) { return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } -// Size of a chunk including header and padding. -static uint32_t ChunkDiskSize(const WebPChunk* chunk) { - assert(chunk->payload_size_ < MAX_CHUNK_PAYLOAD); - return CHUNK_HEADER_SIZE + ((chunk->payload_size_ + 1) & ~1U); -} - static void PutLE16(uint8_t* const data, uint16_t val) { data[0] = (val >> 0) & 0xff; data[1] = (val >> 8) & 0xff; @@ -472,13 +644,12 @@ static WebPMuxError MuxSet(WebPMux* const mux, TAG_ID id, uint32_t nth, WebPChunk chunk; WebPMuxError err = WEBP_MUX_NOT_FOUND; if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + assert(!IsWPI(id)); + InitChunk(&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(FRAME_ID, &mux->images_); - SWITCH_ID_LIST(TILE_ID, &mux->images_); - SWITCH_ID_LIST(IMAGE_ID, &mux->images_); SWITCH_ID_LIST(META_ID, &mux->meta_); if (id == UNKNOWN_ID && size > 4) { // For raw-data unknown chunk, the first four bytes should be the tag to be @@ -518,6 +689,7 @@ static WebPMuxError WebPMuxValidate(const WebPMux* const mux) { int num_tiles; int num_vp8x; int num_images; + int num_alpha; uint32_t flags; WebPMuxError err; @@ -587,6 +759,20 @@ static WebPMuxError WebPMuxValidate(const WebPMux* const mux) { return WEBP_MUX_INVALID_ARGUMENT; } + // ALPHA_FLAG & alpha chunk(s) are consistent. + err = WebPMuxNumNamedElements(mux, kChunks[ALPHA_ID].chunkName, &num_alpha); + if (err != WEBP_MUX_OK) return err; + if (IsNotCompatible(flags & ALPHA_FLAG, num_alpha)) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // num_images & num_alpha_chunks are consistent. + if ((num_alpha > 0) && (num_alpha != num_images)) { + // Note that "num_alpha > 0" is the correct check but "flags && ALPHA_FLAG" + // is NOT, because ALPHA_FLAG is based on first image only. + return WEBP_MUX_INVALID_ARGUMENT; + } + return WEBP_MUX_OK; } @@ -596,6 +782,7 @@ WebPMux* WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data) { const uint8_t* end; TAG_ID id; WebPMux* mux; + WebPMuxImage wpi; // Sanity checks on size and leading bytes. if (data == NULL) return NULL; @@ -622,27 +809,41 @@ WebPMux* WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data) { mux = WebPMuxNew(); if (mux == NULL) return NULL; + InitImage(&wpi); + // Loop over chunks. while (data != end) { WebPChunk chunk; - WebPChunk** chunk_list; uint32_t data_size; InitChunk(&chunk); - if (RecordChunk(&chunk, data, mux_size, copy_data) != WEBP_MUX_OK) { - WebPMuxDelete(mux); - return NULL; - } + if (RecordChunk(&chunk, data, mux_size, copy_data) != WEBP_MUX_OK) goto Err; data_size = ChunkDiskSize(&chunk); - id = GetChunkIdFromTag(chunk.tag_); - chunk_list = GetListFromId(mux, id); - if (chunk_list == NULL) chunk_list = (WebPChunk**)&mux->unknown_; - if (SetNthChunk(&chunk, chunk_list, 0) != WEBP_MUX_OK) { - WebPMuxDelete(mux); - return NULL; + if (IsWPI(id)) { // An image chunk (frame/tile/alpha/vp8). + WebPChunk** wpi_chunk_ptr; + wpi_chunk_ptr = GetImageListFromId(&wpi, id); // Image chunk to be set. + assert(wpi_chunk_ptr != NULL); + if (*wpi_chunk_ptr != NULL) goto Err; // Consecutive alpha chunks or + // consecutive frame/tile chunks. + if (SetNthChunk(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err; + if (id == IMAGE_ID) { + wpi.is_partial_ = 0; // wpi is completely filled. + // Add this to mux->images_ list. + if (SetNthImage(&wpi, &mux->images_, 0) != WEBP_MUX_OK) goto Err; + InitImage(&wpi); // Reset for reading next image. + } else { + wpi.is_partial_ = 1; // wpi is only partially filled. + } + } else { // A non-image chunk. + WebPChunk** chunk_list; + if (wpi.is_partial_) goto Err; // Encountered a non-image chunk before + // getting all chunks of an image. + chunk_list = GetChunkListFromId(mux, id); // List for adding this chunk. + if (chunk_list == NULL) chunk_list = (WebPChunk**)&mux->unknown_; + if (SetNthChunk(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; } data += data_size; @@ -650,16 +851,89 @@ WebPMux* WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data) { } // Validate mux. - if (WebPMuxValidate(mux) != WEBP_MUX_OK) { + if (WebPMuxValidate(mux) != WEBP_MUX_OK) goto Err; + + return mux; // All OK; + + Err: // Something bad happened. + WebPMuxDelete(mux); + return NULL; +} +//------------------------------------------------------------------------------ +// Helper function(s). + +// Outputs image data given data from a webp file (including RIFF header). +static WebPMuxError GetImageData(const uint8_t* data, uint32_t size, + const uint8_t** image_data, + uint32_t* image_size, + const uint8_t** alpha_data, + uint32_t* alpha_size) { + if ((size < TAG_SIZE) || (memcmp(data, "RIFF", TAG_SIZE))) { + // It is NOT webp file data. Return input data as is. + *image_data = data; + *image_size = size; + return WEBP_MUX_OK; + } else { + // It is webp file data. Extract image data from it. + WebPMux* mux; + WebPMuxError err; + mux = WebPMuxCreate(data, size, 0); + if (mux == NULL) return WEBP_MUX_BAD_DATA; + + err = WebPMuxGetImage(mux, image_data, image_size, alpha_data, alpha_size); WebPMuxDelete(mux); - return NULL; + return err; } - return mux; } //------------------------------------------------------------------------------ // Set API(s). +WebPMuxError WebPMuxSetImage(WebPMux* const mux, + const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, uint32_t alpha_size, + int copy_data) { + WebPMuxError err; + WebPChunk chunk; + WebPMuxImage wpi; + const uint8_t* vp8_data; + uint32_t vp8_size; + const int has_alpha = (alpha_data != NULL && alpha_size != 0); + + 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, &vp8_data, &vp8_size, NULL, NULL); + if (err != WEBP_MUX_OK) return err; + + // Delete the existing images. + DeleteAllImages(&mux->images_); + + InitImage(&wpi); + + if (has_alpha) { // Add alpha chunk. + InitChunk(&chunk); + err = AssignData(&chunk, alpha_data, alpha_size, NULL, copy_data, + kChunks[ALPHA_ID].chunkTag); + if (err != WEBP_MUX_OK) return err; + err = SetNthChunk(&chunk, &wpi.alpha_, 1); + if (err != WEBP_MUX_OK) return err; + } + + // Add image chunk. + InitChunk(&chunk); + err = AssignData(&chunk, vp8_data, vp8_size, NULL, copy_data, + kChunks[IMAGE_ID].chunkTag); + if (err != WEBP_MUX_OK) return err; + err = SetNthChunk(&chunk, &wpi.vp8_, 1); + if (err != WEBP_MUX_OK) return err; + + // Add this image to mux. + return SetNthImage(&wpi, &mux->images_, 1); +} + WebPMuxError WebPMuxSetMetadata(WebPMux* const mux, const uint8_t* data, uint32_t size, int copy_data) { WebPMuxError err; @@ -692,8 +966,6 @@ WebPMuxError WebPMuxSetColorProfile(WebPMux* const mux, const uint8_t* data, return MuxSet(mux, ICCP_ID, 1, data, size, NULL, copy_data); } -static WebPMuxError WebPMuxDeleteLoopCount(WebPMux* const mux); - WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { WebPMuxError err; uint8_t* data = NULL; @@ -701,7 +973,7 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; // Delete the existing LOOP chunk(s). - err = WebPMuxDeleteLoopCount(mux); + err = DeleteLoopCount(mux); if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; // Add the given loop count. @@ -718,43 +990,14 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) { //------------------------------------------------------------------------------ // Helper functions. -// Find out absolute indices or frame/chunk and image chunks -// given overall index 'nth'. -// isFrame = true -> This is for a frame. -// isFrame = false -> This is for a tile. -// Returns 0 if error -// 1 on success -static int GetFrameTileAndImageIndices(uint32_t nth, int isFrame, - uint32_t* frame_tile_idx, - uint32_t* image_idx) { - const int multiplier = isFrame ? CHUNKS_PER_FRAME : CHUNKS_PER_TILE; - - assert(frame_tile_idx != NULL); - assert(image_idx != NULL); - - if (nth == 0) { // Both chunks are at the end. - *frame_tile_idx = 0; - *image_idx = 0; - } else { // Position given. - // Frame/Tile chunk is in (multiplier * nth - 1) position in - // mux->images_ list because prior (nth-1) chunks occupy positions - // 1 through multiplier * (nth - 1). - *frame_tile_idx = multiplier * nth - 1; - - // Image chunk is in (multiplier * nth) position in mux->images_ list. - *image_idx = *frame_tile_idx + 1; - } - return 1; -} - static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info, - int isFrame, uint8_t** data, + int is_frame, uint8_t** data, uint32_t* size) { assert(data); assert(size); assert(image_info); - *size = isFrame ? kChunks[FRAME_ID].chunkSize : kChunks[TILE_ID].chunkSize; + *size = is_frame ? kChunks[FRAME_ID].chunkSize : kChunks[TILE_ID].chunkSize; *data = (uint8_t*)malloc(*size); if (*data == NULL) return WEBP_MUX_MEMORY_ERROR; @@ -762,7 +1005,7 @@ static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info, PutLE32(*data, image_info->x_offset_); PutLE32(*data + 4, image_info->y_offset_); - if (isFrame) { + if (is_frame) { PutLE32(*data + 8, image_info->width_); PutLE32(*data + 12, image_info->height_); PutLE32(*data + 16, image_info->duration_); @@ -770,65 +1013,80 @@ static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info, return WEBP_MUX_OK; } -static WebPMuxError WebPMuxDeleteNamedData(WebPMux* const mux, uint32_t nth, - const char* const tag); - static WebPMuxError WebPMuxAddFrameTileInternal(WebPMux* const mux, uint32_t nth, const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, + uint32_t alpha_size, uint32_t x_offset, uint32_t y_offset, uint32_t duration, int copy_data, uint32_t tag) { + WebPChunk chunk; + WebPMuxImage wpi; WebPMuxError err; WebPImageInfo* image_info = NULL; uint8_t* frame_tile_data = NULL; uint32_t frame_tile_data_size = 0; - uint32_t pos1, pos2; - int ok; - const int isFrame = (tag == kChunks[FRAME_ID].chunkTag) ? 1 : 0; - const char* tag_name = isFrame ? "frame" : "tile"; + const uint8_t* vp8_data = NULL; + uint32_t vp8_size = 0; + const int is_frame = (tag == kChunks[FRAME_ID].chunkTag) ? 1 : 0; + const int has_alpha = (alpha_data != NULL && alpha_size != 0); if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } - // Get absolute indexes for frame/title and image chunks - // (because MuxSet() assumes 'nth' is absolute position in a chunk list). - ok = GetFrameTileAndImageIndices(nth, isFrame, &pos1, &pos2); - if (!ok) return WEBP_MUX_MEMORY_ERROR; + // If given data is for a whole webp file, extract only the VP8 data from it. + err = GetImageData(data, size, &vp8_data, &vp8_size, NULL, NULL); + if (err != WEBP_MUX_OK) return err; + + InitImage(&wpi); + + if (has_alpha) { + // Add alpha chunk. + InitChunk(&chunk); + err = AssignData(&chunk, alpha_data, alpha_size, NULL, copy_data, + kChunks[ALPHA_ID].chunkTag); + if (err != WEBP_MUX_OK) return err; + err = SetNthChunk(&chunk, &wpi.alpha_, 1); + if (err != WEBP_MUX_OK) return err; + } // Create image_info object. - image_info = CreateImageInfo(x_offset, y_offset, duration, data, size); + image_info = CreateImageInfo(x_offset, y_offset, duration, vp8_data, + vp8_size); if (image_info == NULL) return WEBP_MUX_MEMORY_ERROR; + // Add image chunk. + InitChunk(&chunk); + err = AssignData(&chunk, vp8_data, vp8_size, image_info, copy_data, + kChunks[IMAGE_ID].chunkTag); + if (err != WEBP_MUX_OK) goto Err; + err = SetNthChunk(&chunk, &wpi.vp8_, 1); + if (err != WEBP_MUX_OK) goto Err; + // Create frame/tile data from image_info. - err = CreateDataFromImageInfo(image_info, isFrame, &frame_tile_data, + err = CreateDataFromImageInfo(image_info, is_frame, &frame_tile_data, &frame_tile_data_size); - if (err != WEBP_MUX_OK) { - free(image_info); - return err; - } + if (err != WEBP_MUX_OK) goto Err; // Add frame/tile chunk (with copy_data = 1). - err = WebPMuxAddChunk(mux, pos1, tag, frame_tile_data, frame_tile_data_size, - NULL, 1); - if (err != WEBP_MUX_OK) { - free(image_info); - free(frame_tile_data); - return err; - } + InitChunk(&chunk); + err = AssignData(&chunk, frame_tile_data, frame_tile_data_size, NULL, 1, tag); + if (err != WEBP_MUX_OK) goto Err; + err = SetNthChunk(&chunk, &wpi.header_, 1); + if (err != WEBP_MUX_OK) goto Err; - // Add image chunk. - err = WebPMuxAddChunk(mux, pos2, kChunks[IMAGE_ID].chunkTag, data, size, - image_info, copy_data); - if (err != WEBP_MUX_OK) { - WebPMuxDeleteNamedData(mux, pos1, tag_name); // Delete frame/tile chunk. - free(image_info); - } + free(frame_tile_data); + // Add this WebPMuxImage to mux. + return SetNthImage(&wpi, &mux->images_, nth); + + Err: // Something bad happened. + free(image_info); free(frame_tile_data); return err; } @@ -836,66 +1094,62 @@ static WebPMuxError WebPMuxAddFrameTileInternal(WebPMux* const mux, //------------------------------------------------------------------------------ // Add API(s). +// TODO(urvang): Think about whether we need 'nth' while adding a frame or tile. + WebPMuxError WebPMuxAddFrame(WebPMux* const mux, uint32_t nth, const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, uint32_t alpha_size, uint32_t x_offset, uint32_t y_offset, uint32_t duration, int copy_data) { - return WebPMuxAddFrameTileInternal(mux, nth, data, size, x_offset, y_offset, - duration, copy_data, - kChunks[FRAME_ID].chunkTag); + return WebPMuxAddFrameTileInternal(mux, nth, data, size, + alpha_data, alpha_size, + x_offset, y_offset, duration, + copy_data, kChunks[FRAME_ID].chunkTag); } WebPMuxError WebPMuxAddTile(WebPMux* const mux, uint32_t nth, const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, uint32_t alpha_size, uint32_t x_offset, uint32_t y_offset, int copy_data) { - return WebPMuxAddFrameTileInternal(mux, nth, data, size, x_offset, y_offset, - 1, copy_data, kChunks[TILE_ID].chunkTag); -} - -WebPMuxError WebPMuxAddNamedData(WebPMux* const mux, uint32_t nth, - const char* const tag, const uint8_t* data, - uint32_t size, int copy_data) { - TAG_ID id; - if (mux == NULL || tag == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - id = GetChunkIdFromName(tag); - if (id == NIL_ID) return WEBP_MUX_INVALID_PARAMETER; - - return WebPMuxAddChunk(mux, nth, kChunks[id].chunkTag, data, size, NULL, - copy_data); + return WebPMuxAddFrameTileInternal(mux, nth, data, size, + alpha_data, alpha_size, + x_offset, y_offset, 1, + copy_data, kChunks[TILE_ID].chunkTag); } //------------------------------------------------------------------------------ // Delete API(s). -static WebPMuxError WebPMuxDeleteNamedData(WebPMux* const mux, uint32_t nth, - const char* const tag) { - TAG_ID id; - WebPChunk** chunk_list; - if ((mux == NULL) || (tag == NULL)) return WEBP_MUX_INVALID_ARGUMENT; - - id = GetChunkIdFromName(tag); - chunk_list = GetListFromId(mux, id); - if (chunk_list == NULL) return WEBP_MUX_INVALID_PARAMETER; - - return DeleteNthChunk(chunk_list, nth, kChunks[id].chunkTag); -} - static WebPMuxError WebPMuxDeleteAllNamedData(WebPMux* const mux, const char* const tag) { TAG_ID id; WebPChunk** chunk_list; + if ((mux == NULL) || (tag == NULL)) return WEBP_MUX_INVALID_ARGUMENT; id = GetChunkIdFromName(tag); - chunk_list = GetListFromId(mux, id); - if (chunk_list == NULL) return WEBP_MUX_INVALID_PARAMETER; + 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].chunkTag); } +WebPMuxError WebPMuxDeleteImage(WebPMux* const mux) { + WebPMuxError err; + + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + err = ValidateForImage(mux); + if (err != WEBP_MUX_OK) return err; + + // All Well, delete Image. + DeleteAllImages(&mux->images_); + return WEBP_MUX_OK; +} + WebPMuxError WebPMuxDeleteMetadata(WebPMux* const mux) { return WebPMuxDeleteAllNamedData(mux, kChunks[META_ID].chunkName); } @@ -904,29 +1158,27 @@ WebPMuxError WebPMuxDeleteColorProfile(WebPMux* const mux) { return WebPMuxDeleteAllNamedData(mux, kChunks[ICCP_ID].chunkName); } -static WebPMuxError WebPMuxDeleteLoopCount(WebPMux* const mux) { +static WebPMuxError DeleteLoopCount(WebPMux* const mux) { return WebPMuxDeleteAllNamedData(mux, kChunks[LOOP_ID].chunkName); } -static WebPMuxError WebPMuxDeleteFrameTileInternal(WebPMux* const mux, - uint32_t nth, - const char* const tag) { - WebPMuxError err; +static WebPMuxError DeleteFrameTileInternal(WebPMux* const mux, + uint32_t nth, + const char* const tag) { + TAG_ID id; + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - // Delete image chunk. - err = WebPMuxDeleteNamedData(mux, nth, kChunks[IMAGE_ID].chunkName); - if (err != WEBP_MUX_OK) return err; - - // Delete frame chunk. - return WebPMuxDeleteNamedData(mux, nth, tag); + id = GetChunkIdFromName(tag); + assert(id == FRAME_ID || id == TILE_ID); + return DeleteNthImage(&mux->images_, nth, id); } WebPMuxError WebPMuxDeleteFrame(WebPMux* const mux, uint32_t nth) { - return WebPMuxDeleteFrameTileInternal(mux, nth, kChunks[FRAME_ID].chunkName); + return DeleteFrameTileInternal(mux, nth, kChunks[FRAME_ID].chunkName); } WebPMuxError WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth) { - return WebPMuxDeleteFrameTileInternal(mux, nth, kChunks[TILE_ID].chunkName); + return DeleteFrameTileInternal(mux, nth, kChunks[TILE_ID].chunkName); } //------------------------------------------------------------------------------ @@ -941,6 +1193,15 @@ static uint32_t ChunksListDiskSize(const WebPChunk* chunk_list) { return size; } +static uint32_t ImageListDiskSize(const WebPMuxImage* wpi_list) { + uint32_t size = 0; + while (wpi_list) { + size += ImageDiskSize(wpi_list); + wpi_list = wpi_list->next_; + } + return size; +} + static uint8_t* EmitChunk(const WebPChunk* const chunk, uint8_t* dst) { assert(chunk); assert(chunk->tag_ != NIL_TAG); @@ -960,6 +1221,26 @@ static uint8_t* EmitChunks(const WebPChunk* chunk_list, uint8_t* dst) { return dst; } +static uint8_t* EmitImage(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. + assert(wpi); + if (wpi->header_ != NULL) dst = EmitChunk(wpi->header_, dst); + if (wpi->alpha_ != NULL) dst = EmitChunk(wpi->alpha_, dst); + if (wpi->vp8_ != NULL) dst = EmitChunk(wpi->vp8_, dst); + return dst; +} + +static uint8_t* EmitImages(const WebPMuxImage* wpi_list, uint8_t* dst) { + while (wpi_list) { + dst = EmitImage(wpi_list, dst); + wpi_list = wpi_list->next_; + } + return dst; +} + static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux, uint32_t flags, uint32_t* width, @@ -967,21 +1248,20 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux, uint32_t max_x = 0; uint32_t max_y = 0; uint64_t image_area = 0; - WebPChunk* image_chunk = NULL; + WebPMuxImage* wpi = NULL; assert(mux != NULL); - assert(mux->images_ != NULL); - assert(width && height); - image_chunk = mux->images_; + wpi = mux->images_; + assert(wpi != NULL); - if (image_chunk->next_) { + if (wpi->next_) { // Aggregate the bounding box for Animation frames & Tiled images. - for (; image_chunk != NULL; image_chunk = image_chunk->next_) { - const WebPImageInfo* const image_info = image_chunk->image_info_; - - if (image_chunk->tag_ != kChunks[IMAGE_ID].chunkTag) continue; + for (; wpi != NULL; wpi = wpi->next_) { + const WebPImageInfo* image_info; + assert(wpi->vp8_ != NULL); + image_info = wpi->vp8_->image_info_; if (image_info != NULL) { const uint32_t max_x_pos = image_info->x_offset_ + image_info->width_; @@ -1007,7 +1287,7 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux, // images. Check that the aggregated image area for individual tiles exactly // matches the image area of the constructed Canvas. However, the area-match // is necessary but not sufficient condition. - if ((flags & TILE_FLAG) && (image_area != (max_x * max_y))) { + if (!!(flags & TILE_FLAG) && (image_area != (max_x * max_y))) { *width = 0; *height = 0; return WEBP_MUX_INVALID_ARGUMENT; @@ -1015,6 +1295,8 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux, } else { // For a single image, extract the width & height from VP8 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, NULL)) { *width = w; @@ -1029,16 +1311,19 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux, // Flags : 4 bytes, // Width : 4 bytes, // Height : 4 bytes. -static WebPMuxError CreateVp8xChunk(WebPMux* const mux) { +static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { WebPMuxError err = WEBP_MUX_OK; uint32_t flags = 0; uint32_t width = 0; uint32_t height = 0; uint8_t data[VP8X_CHUNK_SIZE]; const size_t data_size = VP8X_CHUNK_SIZE; + const WebPMuxImage* images = NULL; + + images = mux->images_; // First image. assert(mux != NULL); - if (mux->images_ == NULL || mux->images_->data_ == NULL) { + if (images == NULL || images->vp8_ == NULL || images->vp8_->data_ == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } @@ -1056,12 +1341,19 @@ static WebPMuxError CreateVp8xChunk(WebPMux* const mux) { flags |= META_FLAG; } - if (mux->images_->tag_ == kChunks[TILE_ID].chunkTag) { - // This is a tiled image. - flags |= TILE_FLAG; - } else if (mux->images_->tag_ == kChunks[FRAME_ID].chunkTag) { - // This is a simple animation image. - flags |= ANIMATION_FLAG; + if (images->header_ != NULL) { + if (images->header_->tag_ == kChunks[TILE_ID].chunkTag) { + // This is a tiled image. + flags |= TILE_FLAG; + } else if (images->header_->tag_ == kChunks[FRAME_ID].chunkTag) { + // This is an image with animation. + flags |= ANIMATION_FLAG; + } + } + + if (images->alpha_ != NULL && images->alpha_->data_ != NULL) { + // This is an image with alpha channel. + flags |= ALPHA_FLAG; } if (flags == 0) { @@ -1106,18 +1398,18 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, uint8_t** output_data, &num_frames); if (err != WEBP_MUX_OK) return err; if (num_frames == 0) { - err = WebPMuxDeleteLoopCount(mux); + err = DeleteLoopCount(mux); if (err != WEBP_MUX_OK) return err; } } // Create VP8X chunk. - err = CreateVp8xChunk(mux); + err = CreateVP8XChunk(mux); if (err != WEBP_MUX_OK) return err; // Allocate data. size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_) - + ChunksListDiskSize(mux->loop_) + ChunksListDiskSize(mux->images_) + + ChunksListDiskSize(mux->loop_) + ImageListDiskSize(mux->images_) + ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE; @@ -1134,7 +1426,7 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, uint8_t** output_data, dst = EmitChunks(mux->vp8x_, dst); dst = EmitChunks(mux->iccp_, dst); dst = EmitChunks(mux->loop_, dst); - dst = EmitChunks(mux->images_, dst); + dst = EmitImages(mux->images_, dst); dst = EmitChunks(mux->meta_, dst); dst = EmitChunks(mux->unknown_, dst); assert(dst == data + size); @@ -1176,12 +1468,11 @@ static WebPMuxError MuxGet(const WebPMux* const mux, TAG_ID id, uint32_t nth, assert(mux != NULL); *data = NULL; *data_size = 0; + assert(!IsWPI(id)); + SWITCH_ID_LIST(VP8X_ID, mux->vp8x_); SWITCH_ID_LIST(ICCP_ID, mux->iccp_); SWITCH_ID_LIST(LOOP_ID, mux->loop_); - SWITCH_ID_LIST(FRAME_ID, mux->images_); - SWITCH_ID_LIST(TILE_ID, mux->images_); - SWITCH_ID_LIST(IMAGE_ID, mux->images_); SWITCH_ID_LIST(META_ID, mux->meta_); SWITCH_ID_LIST(UNKNOWN_ID, mux->unknown_); return WEBP_MUX_NOT_FOUND; @@ -1195,8 +1486,7 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) { if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT; - err = WebPMuxGetNamedData(mux, kChunks[VP8X_ID].chunkName, 1, &data, - &data_size); + err = MuxGet(mux, VP8X_ID, 1, &data, &data_size); if (err == WEBP_MUX_NOT_FOUND) { // Single image case. *flags = 0; return WEBP_MUX_OK; @@ -1210,6 +1500,44 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) { return WEBP_MUX_OK; } +WebPMuxError WebPMuxGetImage(const WebPMux* const mux, + const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, uint32_t* alpha_size) { + WebPMuxError err; + WebPMuxImage* wpi = NULL; + + if (mux == NULL || data == NULL || size == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + *data = NULL; + *size = 0; + + err = ValidateForImage(mux); + if (err != WEBP_MUX_OK) return err; + + // All well. Get the image. + err = GetNthImage((const WebPMuxImage**)&mux->images_, 1, IMAGE_ID, &wpi); + assert(err == WEBP_MUX_OK); // Already tested above. + + // Get alpha chunk (if present & requested). + if (alpha_data != NULL && alpha_size != NULL) { + *alpha_data = NULL; + *alpha_size = 0; + if (wpi->alpha_ != NULL) { + *alpha_data = wpi->alpha_->data_; + *alpha_size = wpi->alpha_->payload_size_; + } + } + + // Get image chunk. + if (wpi->vp8_ != NULL) { + *data = wpi->vp8_->data_; + *size = wpi->vp8_->payload_size_; + } + return WEBP_MUX_OK; +} + WebPMuxError WebPMuxGetMetadata(const WebPMux* const mux, const uint8_t** data, uint32_t* size) { if (mux == NULL || data == NULL || size == NULL) { @@ -1236,8 +1564,7 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, if (mux == NULL || loop_count == NULL) return WEBP_MUX_INVALID_ARGUMENT; - err = WebPMuxGetNamedData(mux, kChunks[LOOP_ID].chunkName, 1, &data, - &data_size); + err = MuxGet(mux, LOOP_ID, 1, &data, &data_size); if (err != WEBP_MUX_OK) return err; if (data_size < kChunks[LOOP_ID].chunkSize) return WEBP_MUX_BAD_DATA; *loop_count = GetLE32(data); @@ -1245,54 +1572,81 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux, return WEBP_MUX_OK; } -WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, - const uint8_t** data, uint32_t* size, - uint32_t* x_offset, uint32_t* y_offset, - uint32_t* duration) { - const uint8_t* frame_data; - uint32_t frame_size; +static WebPMuxError WebPMuxGetFrameTileInternal(const WebPMux* const mux, + uint32_t nth, + const uint8_t** data, + uint32_t* size, + const uint8_t** alpha_data, + uint32_t* alpha_size, + uint32_t* x_offset, + uint32_t* y_offset, + uint32_t* duration, + uint32_t tag) { + const uint8_t* frame_tile_data; + uint32_t frame_tile_size; WebPMuxError err; + WebPMuxImage* wpi; - if (mux == NULL || data == NULL || size == NULL || x_offset == NULL || - y_offset == NULL || duration == NULL) { + const int is_frame = (tag == kChunks[FRAME_ID].chunkTag) ? 1 : 0; + const TAG_ID id = is_frame ? FRAME_ID : TILE_ID; + + if (mux == NULL || data == NULL || size == NULL || + x_offset == NULL || y_offset == NULL || (is_frame && duration == NULL)) { return WEBP_MUX_INVALID_ARGUMENT; } - // Get frame chunk. - err = WebPMuxGetNamedData(mux, kChunks[FRAME_ID].chunkName, nth, &frame_data, - &frame_size); + // Get the nth WebPMuxImage. + err = GetNthImage((const WebPMuxImage**)&mux->images_, nth, id, &wpi); if (err != WEBP_MUX_OK) return err; - if (frame_size < kChunks[FRAME_ID].chunkSize) return WEBP_MUX_BAD_DATA; - *x_offset = GetLE32(frame_data); - *y_offset = GetLE32(frame_data + 4); - *duration = GetLE32(frame_data + 16); + + // Get frame chunk. + assert(wpi->header_ != NULL); // As GetNthImage() already checked header_. + frame_tile_data = wpi->header_->data_; + frame_tile_size = wpi->header_->payload_size_; + + if (frame_tile_size < kChunks[id].chunkSize) return WEBP_MUX_BAD_DATA; + *x_offset = GetLE32(frame_tile_data); + *y_offset = GetLE32(frame_tile_data + 4); + if (is_frame) *duration = GetLE32(frame_tile_data + 16); + + // Get alpha chunk (if present & requested). + if (alpha_data != NULL && alpha_size != NULL) { + *alpha_data = NULL; + *alpha_size = 0; + if (wpi->alpha_ != NULL) { + *alpha_data = wpi->alpha_->data_; + *alpha_size = wpi->alpha_->payload_size_; + } + } // Get image chunk. - return WebPMuxGetNamedData(mux, kChunks[IMAGE_ID].chunkName, nth, data, size); + *data = NULL; + *size = 0; + if (wpi->vp8_ != NULL) { + *data = wpi->vp8_->data_; + *size = wpi->vp8_->payload_size_; + } + + return WEBP_MUX_OK; +} + +WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, + const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, uint32_t* alpha_size, + uint32_t* x_offset, uint32_t* y_offset, + uint32_t* duration) { + return WebPMuxGetFrameTileInternal(mux, nth, data, size, alpha_data, + alpha_size, x_offset, y_offset, duration, + kChunks[FRAME_ID].chunkTag); } WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, uint32_t* alpha_size, uint32_t* x_offset, uint32_t* y_offset) { - const uint8_t* tile_data; - uint32_t tile_size; - WebPMuxError err; - - if (mux == NULL || data == NULL || size == NULL || x_offset == NULL || - y_offset == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Get frame chunk. - err = WebPMuxGetNamedData(mux, kChunks[TILE_ID].chunkName, nth, &tile_data, - &tile_size); - if (err != WEBP_MUX_OK) return err; - if (tile_size < kChunks[TILE_ID].chunkSize) return WEBP_MUX_BAD_DATA; - *x_offset = GetLE32(tile_data); - *y_offset = GetLE32(tile_data + 4); - - // Get image chunk. - return WebPMuxGetNamedData(mux, kChunks[IMAGE_ID].chunkName, nth, data, size); + return WebPMuxGetFrameTileInternal(mux, nth, data, size, alpha_data, + alpha_size, x_offset, y_offset, NULL, + kChunks[TILE_ID].chunkTag); } WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* tag, @@ -1305,31 +1659,20 @@ WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* tag, } id = GetChunkIdFromName(tag); - chunk_list = GetListFromId(mux, id); - - if (chunk_list == NULL) { - *num_elements = 0; + if (IsWPI(id)) { + *num_elements = ListCountImages(mux->images_, id); } else { - *num_elements = ListCountChunks(*chunk_list, kChunks[id].chunkTag); + chunk_list = GetChunkListFromId(mux, id); + if (chunk_list == NULL) { + *num_elements = 0; + } else { + *num_elements = ListCountChunks(*chunk_list, kChunks[id].chunkTag); + } } return WEBP_MUX_OK; } -WebPMuxError WebPMuxGetNamedData(const WebPMux* const mux, const char* tag, - uint32_t nth, const uint8_t** data, - uint32_t* size) { - TAG_ID id; - if (mux == NULL || tag == NULL || data == NULL || size == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - id = GetChunkIdFromName(tag); - if (id == NIL_ID) return WEBP_MUX_INVALID_PARAMETER; - - return MuxGet(mux, id, nth, data, size); -} - //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/webp/mux.h b/src/webp/mux.h index 9e884b1a..c3afb81c 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -18,8 +18,8 @@ // int copy_data = 0; // WebPMux* mux = WebPMuxNew(); // // ... (Prepare image data). -// WebPMuxAddNamedData(mux, 1, "image", image_data, image_data_size, -// copy_data); +// WebPMuxSetImage(mux, image_data, image_data_size, alpha_data, alpha_size, +// copy_data); // // ... (Prepare ICCP color profile data). // WebPMuxSetColorProfile(mux, icc_data, icc_data_size, copy_data); // // ... (Prepare XMP metadata). @@ -35,7 +35,8 @@ // int copy_data = 0; // // ... (Read data from file). // WebPMux* mux = WebPMuxCreate(data, data_size, copy_data); -// WebPMuxGetNamedData(mux, "image", 1, &image_data, &image_data_size); +// WebPMuxGetImage(mux, &image_data, &image_data_size, +// &alpha_data, &alpha_size); // // ... (Consume image_data; e.g. call WebPDecode() to decode the data). // WebPMuxGetColorProfile(mux, &icc_data, &icc_data_size); // // ... (Consume icc_data). @@ -67,7 +68,8 @@ typedef enum { TILE_FLAG = 0x00000001, ANIMATION_FLAG = 0x00000002, ICCP_FLAG = 0x00000004, - META_FLAG = 0x00000008 + META_FLAG = 0x00000008, + ALPHA_FLAG = 0x00000010 } FeatureFlags; typedef struct WebPMux WebPMux; // main opaque object. @@ -100,6 +102,57 @@ WEBP_EXTERN(void) WebPMuxDelete(WebPMux* const mux); WEBP_EXTERN(WebPMux*) WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data); +//------------------------------------------------------------------------------ +// Single Image. + +// Sets the image in the mux object. Any existing images (including frame/tile) +// 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 +// 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) +// alpha_size - (in) size of alpha chunk data +// copy_data - (in) value 1 indicates given data WILL copied to the mux, and +// value 0 indicates data will NOT be copied. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or data is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(WebPMux* const mux, + const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, + uint32_t alpha_size, int copy_data); + +// Gets a reference to the image in the mux object. +// The caller should NOT free the returned data. +// Parameters: +// mux - (in) object from which the image is to be fetched +// data - (out) the returned image data +// size - (out) size of the returned image data +// alpha_data - (in) the returned alpha data of the image (if present) +// alpha_size - (in) size of alpha chunk data +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if either of mux, data or size is NULL +// OR if mux contains animation/tiling. +// WEBP_MUX_NOT_FOUND - if image is not present in mux object. +// WEBP_MUX_OK - on success. +WEBP_EXTERN(WebPMuxError) WebPMuxGetImage(const WebPMux* const mux, + const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, + uint32_t* alpha_size); + +// Deletes the image in the mux object. +// Parameters: +// mux - (in/out) object from which the image is to be deleted +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL +// OR if mux contains animation/tiling. +// WEBP_MUX_NOT_FOUND - if image is not present in mux object. +// WEBP_MUX_OK - on success. +WEBP_EXTERN(WebPMuxError) WebPMuxDeleteImage(WebPMux* const mux); + //------------------------------------------------------------------------------ // XMP Metadata. @@ -192,8 +245,12 @@ 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 +// 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 +// (non-animated & non-tiled) // size - (in) size of frame chunk data +// alpha_data - (in) the alpha data corresponding to frame image (if present) +// alpha_size - (in) size of alpha chunk data // x_offset - (in) x-offset of the frame to be added // y_offset - (in) y-offset of the frame to be added // duration - (in) duration of the frame to be added (in milliseconds) @@ -206,9 +263,22 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* const mux); // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxAddFrame(WebPMux* const mux, uint32_t nth, const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, + uint32_t alpha_size, uint32_t x_offset, uint32_t y_offset, uint32_t duration, int copy_data); +// TODO(urvang): Create a struct as follows to reduce argument list size: +// typedef struct { +// int nth; +// uint8_t* data; +// uint32_t data_size; +// uint8_t* alpha; +// uint32_t alpha_size; +// uint32_t x_offset, y_offset; +// uint32_t duration; +// } FrameInfo; + // Gets a reference to the nth animation frame from the mux object. // The caller should NOT free the returned data. // nth=0 has a special meaning - last position. @@ -217,6 +287,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxAddFrame(WebPMux* const mux, uint32_t nth, // nth - (in) index of the frame in the mux object // data - (out) the returned image data // size - (out) size of the returned image data +// alpha_data - (in) the alpha data corresponding to frame image (if present) +// alpha_size - (in) size of alpha chunk data // x_offset - (out) x-offset of the returned frame // y_offset - (out) y-offset of the returned frame // duration - (out) duration of the returned frame (in milliseconds) @@ -229,6 +301,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxAddFrame(WebPMux* const mux, uint32_t nth, WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth, const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, + uint32_t* alpha_size, uint32_t* x_offset, uint32_t* y_offset, uint32_t* duration); @@ -277,8 +351,12 @@ 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 +// 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 +// (non-animated & non-tiled) // size - (in) size of tile chunk data +// alpha_data - (in) the alpha data corresponding to tile image (if present) +// alpha_size - (in) size of alpha chunk data // x_offset - (in) x-offset of the tile to be added // y_offset - (in) y-offset of the tile to be added // copy_data - (in) value 1 indicates given data WILL copied to the mux, and @@ -290,6 +368,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* const mux, // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxAddTile(WebPMux* const mux, uint32_t nth, const uint8_t* data, uint32_t size, + const uint8_t* alpha_data, + uint32_t alpha_size, uint32_t x_offset, uint32_t y_offset, int copy_data); @@ -301,6 +381,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxAddTile(WebPMux* const mux, uint32_t nth, // nth - (in) index of the tile in the mux object // data - (out) the returned image data // size - (out) size of the returned image data +// alpha_data - (in) the alpha data corresponding to tile image (if present) +// alpha_size - (in) size of alpha chunk data // x_offset - (out) x-offset of the returned tile // y_offset - (out) y-offset of the returned tile // Returns: @@ -311,6 +393,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxAddTile(WebPMux* const mux, uint32_t nth, // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxGetTile(const WebPMux* const mux, uint32_t nth, const uint8_t** data, uint32_t* size, + const uint8_t** alpha_data, + uint32_t* alpha_size, uint32_t* x_offset, uint32_t* y_offset); @@ -329,45 +413,6 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth); //------------------------------------------------------------------------------ // Misc Utilities. -// Adds a chunk with given tag at nth position. -// nth=0 has a special meaning - last position. -// Parameters: -// mux - (in/out) object to which a chunk is to be added -// nth - (in) The position at which the chunk is to be added. -// tag - (in) tag name specifying the type of chunk -// data - (in) the chunk data -// size - (in) size of tile chunk data -// copy_data - (in) value 1 indicates given data WILL copied to the mux, and -// value 0 indicates data will NOT be copied. -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if either mux, tag or data is NULL -// WEBP_MUX_INVALID_PARAMETER - if tag is invalid. -// WEBP_MUX_MEMORY_ERROR - on memory allocation error. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxAddNamedData(WebPMux* const mux, uint32_t nth, - const char* const tag, - const uint8_t* data, - uint32_t size, int copy_data); - -// Gets a reference to the nth chunk having tag value tag in the mux object. -// The caller should NOT free the returned data. -// nth=0 has a special meaning - last position. -// Parameters: -// mux - (in) object from which a chunk is to be fetched -// tag - (in) tag name specifying which type of chunk is to be fetched -// nth - (in) chunk index corresponding to the specified tag to be fetched -// data - (out) raw data of the chunk returned -// size - (out) size of the returned data -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if either mux, tag, data or size is NULL -// WEBP_MUX_NOT_FOUND - If there are less than nth named elements in the mux -// object. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxGetNamedData(const WebPMux* const mux, - const char* tag, uint32_t nth, - const uint8_t** data, - uint32_t* size); - // Gets the feature flags from the mux object. // Parameters: // mux - (in) object from which the features are to be fetched