From d0c79f0552da90817623df32df80e71452d30b46 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Thu, 23 Aug 2012 16:28:36 +0530 Subject: [PATCH] Mux API change: Create common APIs for image, frame and tile. Change-Id: I709ad752133094bd5bc89dd9c832ff79802aac68 --- README.mux | 2 +- examples/webpmux.c | 92 ++++++------- src/mux/muxedit.c | 296 +++++++++++++++++------------------------- src/mux/muxi.h | 10 +- src/mux/muxinternal.c | 43 +++--- src/mux/muxread.c | 121 ++++++++--------- src/webp/mux.h | 165 +++++++---------------- 7 files changed, 286 insertions(+), 443 deletions(-) diff --git a/README.mux b/README.mux index ac8db11c..965b772c 100644 --- a/README.mux +++ b/README.mux @@ -94,7 +94,7 @@ 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, copy_data); - WebPMuxGetImage(mux, &image); + WebPMuxGetFrame(mux, 1, &image); // ... (Consume image; e.g. call WebPDecode() to decode the data). WebPMuxGetChunk(mux, "ICCP", &icc_profile); // ... (Consume icc_profile). diff --git a/examples/webpmux.c b/examples/webpmux.c index 14ad07ee..87e4bed3 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -142,6 +142,12 @@ static int IsNotCompatible(int count1, int count2) { return err; \ } +#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \ + if (err != WEBP_MUX_OK) { \ + fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ + return err; \ + } + #define ERROR_GOTO1(ERR_MSG, LABEL) \ do { \ fprintf(stderr, ERR_MSG); \ @@ -183,51 +189,36 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { if (flag & ALPHA_FLAG) printf(" transparency"); printf("\n"); - if (flag & ANIMATION_FLAG) { + if ((flag & ANIMATION_FLAG) || (flag & TILE_FLAG)) { + const int is_anim = !!(flag & ANIMATION_FLAG); + const WebPChunkId id = is_anim ? WEBP_CHUNK_FRAME : WEBP_CHUNK_TILE; + const char* const type_str = is_anim ? "frame" : "tile"; int nFrames; - int loop_count; - err = WebPMuxGetLoopCount(mux, &loop_count); - RETURN_IF_ERROR("Failed to retrieve loop count\n"); - printf("Loop Count : %d\n", loop_count); - err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames); - RETURN_IF_ERROR("Failed to retrieve number of frames\n"); + if (is_anim) { + int loop_count; + err = WebPMuxGetLoopCount(mux, &loop_count); + RETURN_IF_ERROR("Failed to retrieve loop count\n"); + printf("Loop Count : %d\n", loop_count); + } - printf("Number of frames: %d\n", nFrames); + err = WebPMuxNumChunks(mux, id, &nFrames); + RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str); + + printf("Number of %ss: %d\n", type_str, nFrames); if (nFrames > 0) { int i; - printf("No.: x_offset y_offset duration image_size"); - printf("\n"); + printf("No.: x_offset y_offset "); + if (is_anim) printf("duration "); + printf("image_size\n"); for (i = 1; i <= nFrames; i++) { WebPMuxFrameInfo frame; err = WebPMuxGetFrame(mux, i, &frame); - RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i); - printf("%3d: %8d %8d %8d %10zu", i, frame.x_offset_, frame.y_offset_, - frame.duration_, frame.bitstream_.size_); + RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); + printf("%3d: %8d %8d ", i, frame.x_offset_, frame.y_offset_); + if (is_anim) printf("%8d ", frame.duration_); + printf("%10zu\n", frame.bitstream_.size_); WebPDataClear(&frame.bitstream_); - printf("\n"); - } - } - } - - if (flag & TILE_FLAG) { - int nTiles; - err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles); - RETURN_IF_ERROR("Failed to retrieve number of tiles\n"); - - printf("Number of tiles: %d\n", nTiles); - if (nTiles > 0) { - int i; - printf("No.: x_offset y_offset image_size"); - printf("\n"); - for (i = 1; i <= nTiles; i++) { - WebPMuxFrameInfo tile; - err = WebPMuxGetTile(mux, i, &tile); - RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i); - printf("%3d: %8d %8d %10zu", - i, tile.x_offset_, tile.y_offset_, tile.bitstream_.size_); - WebPDataClear(&tile.bitstream_); - printf("\n"); } } } @@ -247,10 +238,10 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { } if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) { - WebPData bitstream; - err = WebPMuxGetImage(mux, &bitstream); + WebPMuxFrameInfo image; + err = WebPMuxGetFrame(mux, 1, &image); RETURN_IF_ERROR("Failed to retrieve the image\n"); - printf("Size of the image (with alpha): %zu\n", bitstream.size_); + printf("Size of the image (with alpha): %zu\n", image.bitstream_.size_); } return WEBP_MUX_OK; @@ -693,6 +684,7 @@ static int GetFrameTile(const WebPMux* mux, WebPMux* mux_single = NULL; long num = 0; int ok = 1; + const WebPChunkId id = isFrame ? WEBP_CHUNK_FRAME : WEBP_CHUNK_TILE; WebPMuxFrameInfo info; WebPDataInit(&info.bitstream_); @@ -701,18 +693,11 @@ static int GetFrameTile(const WebPMux* mux, ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet); } - if (isFrame) { - err = WebPMuxGetFrame(mux, num, &info); - if (err != WEBP_MUX_OK) { - ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n", - ErrorString(err), num, ErrGet); - } - } else { - err = WebPMuxGetTile(mux, num, &info); - if (err != WEBP_MUX_OK) { - ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n", - ErrorString(err), num, ErrGet); - } + err = WebPMuxGetFrame(mux, num, &info); + if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND; + if (err != WEBP_MUX_OK) { + ERROR_GOTO3("ERROR (%s): Could not get frame %ld.\n", + ErrorString(err), num, ErrGet); } mux_single = WebPMuxNew(); @@ -726,6 +711,7 @@ static int GetFrameTile(const WebPMux* mux, ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n", ErrorString(err), ErrGet); } + ok = WriteWebP(mux_single, config->output_); ErrGet: @@ -808,6 +794,7 @@ static int Process(const WebPMuxConfig* config) { WebPDataClear(&frame.bitstream_); ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2); } + frame.id = WEBP_CHUNK_FRAME; err = WebPMuxPushFrame(mux, &frame, 1); WebPDataClear(&frame.bitstream_); if (err != WEBP_MUX_OK) { @@ -836,7 +823,8 @@ static int Process(const WebPMuxConfig* config) { WebPDataClear(&tile.bitstream_); ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2); } - err = WebPMuxPushTile(mux, &tile, 1); + tile.id = WEBP_CHUNK_TILE; + err = WebPMuxPushFrame(mux, &tile, 1); WebPDataClear(&tile.bitstream_); if (err != WEBP_MUX_OK) { ERROR_GOTO3("ERROR (%s): Could not add a tile at index %d.\n", diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 6796ac85..89098e7b 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -205,61 +205,6 @@ static WebPMuxError DeleteLoopCount(WebPMux* const mux) { //------------------------------------------------------------------------------ // Set API(s). -WebPMuxError WebPMuxSetImage(WebPMux* mux, - const WebPData* bitstream, int copy_data) { - WebPMuxError err; - WebPChunk chunk; - WebPMuxImage wpi; - WebPData image; - WebPData alpha; - int is_lossless; - int image_tag; - - if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || - bitstream->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // If given data is for a whole webp file, - // extract only the VP8/VP8L data from it. - err = GetImageData(bitstream, &image, &alpha, &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_); - - MuxImageInit(&wpi); - - if (alpha.bytes_ != NULL) { // Add alpha chunk. - ChunkInit(&chunk); - err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.alpha_, 1); - if (err != WEBP_MUX_OK) goto Err; - } - - // Add image chunk. - ChunkInit(&chunk); - err = ChunkAssignData(&chunk, &image, copy_data, image_tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.img_, 1); - if (err != WEBP_MUX_OK) goto Err; - - // Add this image to mux. - err = MuxImagePush(&wpi, &mux->images_); - if (err != WEBP_MUX_OK) goto Err; - - // All OK. - return WEBP_MUX_OK; - - Err: - // Something bad happened. - ChunkRelease(&chunk); - MuxImageRelease(&wpi); - return err; -} - WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4], const WebPData* chunk_data, int copy_data) { const CHUNK_INDEX idx = ChunkGetIndexFromFourCC(fourcc); @@ -278,105 +223,60 @@ WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4], return MuxSet(mux, idx, 1, chunk_data, copy_data); } - -WebPMuxError WebPMuxSetLoopCount(WebPMux* mux, int loop_count) { +// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'. +static WebPMuxError AddDataToChunkList( + const WebPData* const data, int copy_data, uint32_t tag, + WebPChunk** chunk_list) { + WebPChunk chunk; WebPMuxError err; - uint8_t* data = NULL; - - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT; - - // Delete the existing LOOP chunk(s). - err = DeleteLoopCount(mux); - if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; - - // Add the given loop count. - data = (uint8_t*)malloc(kChunks[IDX_LOOP].size); - if (data == NULL) return WEBP_MUX_MEMORY_ERROR; - - PutLE16(data, loop_count); - err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, - kChunks[IDX_LOOP].size, 1); - free(data); + ChunkInit(&chunk); + err = ChunkAssignData(&chunk, data, copy_data, tag); + if (err != WEBP_MUX_OK) goto Err; + err = ChunkSetNth(&chunk, chunk_list, 1); + if (err != WEBP_MUX_OK) goto Err; + return WEBP_MUX_OK; + Err: + ChunkRelease(&chunk); return err; } -static WebPMuxError MuxPushFrameTileInternal( - WebPMux* const mux, const WebPMuxFrameInfo* const frame_tile_info, - int copy_data, uint32_t tag) { - const WebPData* bitstream; - int x_offset, y_offset, duration; - WebPChunk chunk; - WebPData image; - WebPData alpha; +// Extracts image & alpha data from the given bitstream and then sets wpi.alpha_ +// and wpi.img_ appropriately. +static WebPMuxError SetAlphaAndImageChunks( + const WebPData* const bitstream, int copy_data, WebPMuxImage* const wpi) { + int is_lossless = 0; + WebPData image, alpha; + WebPMuxError err = GetImageData(bitstream, &image, &alpha, &is_lossless); + const int image_tag = + is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; + if (err != WEBP_MUX_OK) return err; + if (alpha.bytes_ != NULL) { + err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag, + &wpi->alpha_); + if (err != WEBP_MUX_OK) return err; + } + return AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_); +} + +WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, + int copy_data) { WebPMuxImage wpi; WebPMuxError err; - WebPData frame_tile; - const int is_frame = (tag == kChunks[IDX_FRAME].tag) ? 1 : 0; - int is_lossless; - int image_tag; // Sanity checks. - if (mux == NULL || frame_tile_info == NULL) { + if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || + bitstream->size_ > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; } - bitstream = &frame_tile_info->bitstream_; - x_offset = frame_tile_info->x_offset_; - y_offset = frame_tile_info->y_offset_; - duration = is_frame ? frame_tile_info->duration_ : 1 /* unused */; - - if (bitstream->bytes_ == NULL || bitstream->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET || - y_offset < 0 || y_offset >= MAX_POSITION_OFFSET || - (is_frame && (duration <= 0 || duration > MAX_DURATION))) { - return WEBP_MUX_INVALID_ARGUMENT; + if (mux->images_ != NULL) { + // Only one 'simple image' can be added in mux. So, remove present images. + MuxImageDeleteAll(&mux->images_); } - // Snap offsets to even positions. - x_offset &= ~1; - y_offset &= ~1; - - // If given data is for a whole webp file, - // extract only the VP8/VP8L data from it. - err = GetImageData(bitstream, &image, &alpha, &is_lossless); - if (err != WEBP_MUX_OK) return err; - image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; - - WebPDataInit(&frame_tile); - ChunkInit(&chunk); MuxImageInit(&wpi); - - if (alpha.bytes_ != NULL) { - // Add alpha chunk. - err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.alpha_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.alpha_ now. - } - - // Add image chunk. - err = ChunkAssignData(&chunk, &image, copy_data, image_tag); + err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi); if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.img_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.img_ now. - - // Create frame/tile data. - err = CreateFrameTileData(&image, x_offset, y_offset, duration, is_lossless, - is_frame, &frame_tile); - if (err != WEBP_MUX_OK) goto Err; - - // Add frame/tile chunk (with copy_data = 1). - err = ChunkAssignData(&chunk, &frame_tile, 1, tag); - if (err != WEBP_MUX_OK) goto Err; - WebPDataClear(&frame_tile); - err = ChunkSetNth(&chunk, &wpi.header_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.header_ now. // Add this WebPMuxImage to mux. err = MuxImagePush(&wpi, &mux->images_); @@ -386,61 +286,103 @@ static WebPMuxError MuxPushFrameTileInternal( return WEBP_MUX_OK; Err: // Something bad happened. - WebPDataClear(&frame_tile); - ChunkRelease(&chunk); MuxImageRelease(&wpi); return err; } -WebPMuxError WebPMuxPushFrame(WebPMux* mux, - const WebPMuxFrameInfo* frame, +WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data) { - return MuxPushFrameTileInternal(mux, frame, copy_data, - kChunks[IDX_FRAME].tag); + WebPMuxImage wpi; + WebPMuxError err; + int is_frame; + const WebPData* const bitstream = &frame->bitstream_; + + // Sanity checks. + if (mux == NULL || frame == NULL) return WEBP_MUX_INVALID_ARGUMENT; + + is_frame = (frame->id == WEBP_CHUNK_FRAME); + if (!(is_frame || (frame->id == WEBP_CHUNK_TILE))) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (bitstream->bytes_ == NULL || bitstream->size_ > MAX_CHUNK_PAYLOAD) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + if (mux->images_ != NULL) { + const WebPMuxImage* const image = mux->images_; + const uint32_t image_id = (image->header_ != NULL) ? + ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE; + if (image_id != frame->id) { + return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types. + } + } + + MuxImageInit(&wpi); + err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi); + if (err != WEBP_MUX_OK) goto Err; + assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. + + { + const int is_lossless = (wpi.img_->tag_ == kChunks[IDX_VP8L].tag); + const int x_offset = frame->x_offset_ & ~1; // Snap offsets to even. + const int y_offset = frame->y_offset_ & ~1; + const int duration = is_frame ? frame->duration_ : 1 /* unused */; + const uint32_t tag = kChunks[is_frame ? IDX_FRAME : IDX_TILE].tag; + WebPData frame_tile; + if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET || + y_offset < 0 || y_offset >= MAX_POSITION_OFFSET || + (duration <= 0 || duration > MAX_DURATION)) { + err = WEBP_MUX_INVALID_ARGUMENT; + goto Err; + } + err = CreateFrameTileData(&wpi.img_->data_, x_offset, y_offset, duration, + is_lossless, is_frame, &frame_tile); + if (err != WEBP_MUX_OK) goto Err; + // Add frame/tile chunk (with copy_data = 1). + err = AddDataToChunkList(&frame_tile, 1, tag, &wpi.header_); + WebPDataClear(&frame_tile); // frame_tile owned by wpi.header_ now. + if (err != WEBP_MUX_OK) goto Err; + } + + // Add this WebPMuxImage to mux. + err = MuxImagePush(&wpi, &mux->images_); + if (err != WEBP_MUX_OK) goto Err; + + // All is well. + return WEBP_MUX_OK; + + Err: // Something bad happened. + MuxImageRelease(&wpi); + return err; } -WebPMuxError WebPMuxPushTile(WebPMux* mux, - const WebPMuxFrameInfo* tile, - int copy_data) { - return MuxPushFrameTileInternal(mux, tile /*unused duration*/, copy_data, - kChunks[IDX_TILE].tag); +WebPMuxError WebPMuxSetLoopCount(WebPMux* mux, int loop_count) { + WebPMuxError err; + uint8_t data[LOOP_CHUNK_SIZE]; + + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT; + + // Delete the existing LOOP chunk(s). + err = DeleteLoopCount(mux); + if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; + + // Add the given loop count. + PutLE16(data, loop_count); + return MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, sizeof(data), 1); } //------------------------------------------------------------------------------ // Delete API(s). -WebPMuxError WebPMuxDeleteImage(WebPMux* mux) { - WebPMuxError err; - - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - err = MuxValidateForImage(mux); - if (err != WEBP_MUX_OK) return err; - - // All well, delete image. - MuxImageDeleteAll(&mux->images_); - return WEBP_MUX_OK; -} - WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) { return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc)); } -static WebPMuxError DeleteFrameTileInternal(WebPMux* const mux, uint32_t nth, - CHUNK_INDEX idx) { - const WebPChunkId id = kChunks[idx].id; - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - assert(idx == IDX_FRAME || idx == IDX_TILE); - return MuxImageDeleteNth(&mux->images_, nth, id); -} - WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, IDX_FRAME); -} - -WebPMuxError WebPMuxDeleteTile(WebPMux* mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, IDX_TILE); + if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; + return MuxImageDeleteNth(&mux->images_, nth); } //------------------------------------------------------------------------------ diff --git a/src/mux/muxi.h b/src/mux/muxi.h index 603e4547..b9422d3f 100644 --- a/src/mux/muxi.h +++ b/src/mux/muxi.h @@ -199,6 +199,7 @@ WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi); void MuxImageDeleteAll(WebPMuxImage** const wpi_list); // Count number of images matching the given tag id in the 'wpi_list'. +// If id == WEBP_CHUNK_NIL, all images will be matched. int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id); // Check if given ID corresponds to an image related chunk. @@ -228,13 +229,12 @@ static WEBP_INLINE WebPChunk** MuxImageGetListFromId( // Pushes 'wpi' at the end of 'wpi_list'. WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list); -// Delete nth image in the image list with given tag id. -WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id); +// Delete nth image in the image list. +WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth); -// Get nth image in the image list with given tag id. +// Get nth image in the image list. WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id, WebPMuxImage** wpi); + WebPMuxImage** wpi); // Total size of the given image. size_t MuxImageDiskSize(const WebPMuxImage* const wpi); diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 13d9cfe0..20054044 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -109,7 +109,7 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) { // 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. +// Returns true if nth chunk was found. static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth, WebPChunk*** const location) { uint32_t count = 0; @@ -275,10 +275,14 @@ int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) { int count = 0; const WebPMuxImage* current; for (current = wpi_list; current != NULL; current = current->next_) { - const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(current, id); - if (wpi_chunk != NULL) { - const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); - if (wpi_chunk_id == id) ++count; + if (id == WEBP_CHUNK_NIL) { + ++count; // Special case: count all images. + } else { + const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(current, id); + if (wpi_chunk != NULL) { + const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); + if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'. + } } } return count; @@ -286,34 +290,22 @@ int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) { // 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. +// Returns true if nth image was found. static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id, WebPMuxImage*** const location) { uint32_t count = 0; assert(wpi_list); *location = wpi_list; - // Search makes sense only for the following. - assert(id == WEBP_CHUNK_FRAME || id == WEBP_CHUNK_TILE || - id == WEBP_CHUNK_IMAGE); - assert(id != WEBP_CHUNK_IMAGE || nth == 1); - if (nth == 0) { - nth = MuxImageCount(*wpi_list, id); + nth = MuxImageCount(*wpi_list, WEBP_CHUNK_NIL); if (nth == 0) return 0; // Not found. } while (*wpi_list) { WebPMuxImage* const cur_wpi = *wpi_list; - const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(cur_wpi, id); - if (wpi_chunk != NULL) { - const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_); - if (wpi_chunk_id == id) { - ++count; - if (count == nth) return 1; // Found. - } - } + ++count; + if (count == nth) return 1; // Found. wpi_list = &cur_wpi->next_; *location = wpi_list; } @@ -361,10 +353,9 @@ void MuxImageDeleteAll(WebPMuxImage** const wpi_list) { } } -WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id) { +WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth) { assert(wpi_list); - if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) { + if (!SearchImageToGetOrDelete(wpi_list, nth, &wpi_list)) { return WEBP_MUX_NOT_FOUND; } *wpi_list = MuxImageDelete(*wpi_list); @@ -375,10 +366,10 @@ WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth, // MuxImage reader methods. WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id, WebPMuxImage** wpi) { + WebPMuxImage** wpi) { assert(wpi_list); assert(wpi); - if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id, + if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, (WebPMuxImage***)&wpi_list)) { return WEBP_MUX_NOT_FOUND; } diff --git a/src/mux/muxread.c b/src/mux/muxread.c index 9f08ea32..53e5be89 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -190,6 +190,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, //------------------------------------------------------------------------------ // Get API(s). +// TODO(urvang): Change the behavior of this to return ALPHA_FLAG when the mux +// doesn't contain a VP8X chunk, but does contain a VP8L chunk with real alpha. WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) { WebPData data; WebPMuxError err; @@ -200,10 +202,7 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) { // Check if VP8X chunk is present. err = MuxGet(mux, IDX_VP8X, 1, &data); if (err == WEBP_MUX_NOT_FOUND) { - // Check if VP8/VP8L chunk is present. - err = WebPMuxGetImage(mux, &data); - WebPDataClear(&data); - return err; + return MuxValidateForImage(mux); // Check if a single image is present. } else if (err != WEBP_MUX_OK) { return err; } @@ -230,7 +229,7 @@ static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width, } // Assemble a single image WebP bitstream from 'wpi'. -static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi, +static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, WebPData* const bitstream) { uint8_t* dst; @@ -270,25 +269,6 @@ static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi, return WEBP_MUX_OK; } -WebPMuxError WebPMuxGetImage(const WebPMux* mux, WebPData* bitstream) { - WebPMuxError err; - WebPMuxImage* wpi = NULL; - - if (mux == NULL || bitstream == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - err = MuxValidateForImage(mux); - if (err != WEBP_MUX_OK) return err; - - // All well. Get the image. - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, WEBP_CHUNK_IMAGE, - &wpi); - assert(err == WEBP_MUX_OK); // Already tested above. - - return SynthesizeBitstream(wpi, bitstream); -} - WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4], WebPData* chunk_data) { const CHUNK_INDEX idx = ChunkGetIndexFromFourCC(fourcc); @@ -306,6 +286,56 @@ WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4], } } +static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const info) { + // Set some defaults for unrelated fields. + info->x_offset_ = 0; + info->y_offset_ = 0; + info->duration_ = 1; + // Extract data for related fields. + info->id = ChunkGetIdFromTag(wpi->img_->tag_); + return SynthesizeBitstream(wpi, &info->bitstream_); +} + +static WebPMuxError MuxGetFrameTileInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const frame) { + const int is_frame = (wpi->header_->tag_ == kChunks[IDX_FRAME].tag); + const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE; + const WebPData* frame_tile_data; + assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame(). + // Get frame/tile chunk. + frame_tile_data = &wpi->header_->data_; + if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA; + // Extract info. + frame->x_offset_ = 2 * GetLE24(frame_tile_data->bytes_ + 0); + frame->y_offset_ = 2 * GetLE24(frame_tile_data->bytes_ + 3); + frame->duration_ = is_frame ? 1 + GetLE24(frame_tile_data->bytes_ + 12) : 1; + frame->id = ChunkGetIdFromTag(wpi->header_->tag_); + return SynthesizeBitstream(wpi, &frame->bitstream_); +} + +WebPMuxError WebPMuxGetFrame( + const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) { + WebPMuxError err; + WebPMuxImage* wpi; + + // Sanity checks. + if (mux == NULL || frame == NULL) { + return WEBP_MUX_INVALID_ARGUMENT; + } + + // Get the nth WebPMuxImage. + err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi); + if (err != WEBP_MUX_OK) return err; + + // Get frame info. + if (wpi->header_ == NULL) { + return MuxGetImageInternal(wpi, frame); + } else { + return MuxGetFrameTileInternal(wpi, frame); + } +} + WebPMuxError WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count) { WebPData image; WebPMuxError err; @@ -320,49 +350,6 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count) { return WEBP_MUX_OK; } -static WebPMuxError MuxGetFrameTileInternal( - const WebPMux* const mux, uint32_t nth, - WebPMuxFrameInfo* const frame_tile_info, uint32_t tag) { - const WebPData* frame_tile_data; - WebPMuxError err; - WebPMuxImage* wpi; - - const int is_frame = (tag == kChunks[WEBP_CHUNK_FRAME].tag) ? 1 : 0; - const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE; - const WebPChunkId id = kChunks[idx].id; - - if (mux == NULL || frame_tile_info == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Get the nth WebPMuxImage. - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, id, &wpi); - if (err != WEBP_MUX_OK) return err; - - // Get frame chunk. - assert(wpi->header_ != NULL); // As MuxImageGetNth() already checked header_. - frame_tile_data = &wpi->header_->data_; - - if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA; - frame_tile_info->x_offset_ = 2 * GetLE24(frame_tile_data->bytes_ + 0); - frame_tile_info->y_offset_ = 2 * GetLE24(frame_tile_data->bytes_ + 3); - if (is_frame) { - frame_tile_info->duration_ = 1 + GetLE24(frame_tile_data->bytes_ + 12); - } - - return SynthesizeBitstream(wpi, &frame_tile_info->bitstream_); -} - -WebPMuxError WebPMuxGetFrame(const WebPMux* mux, uint32_t nth, - WebPMuxFrameInfo* frame) { - return MuxGetFrameTileInternal(mux, nth, frame, kChunks[IDX_FRAME].tag); -} - -WebPMuxError WebPMuxGetTile(const WebPMux* mux, uint32_t nth, - WebPMuxFrameInfo* tile) { - return MuxGetFrameTileInternal(mux, nth, tile, kChunks[IDX_TILE].tag); -} - // Get chunk index from chunk id. Returns IDX_NIL if not found. static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) { int i; diff --git a/src/webp/mux.h b/src/webp/mux.h index 81e19397..ea7e10cd 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -35,7 +35,7 @@ // int copy_data = 0; // // ... (Read data from file). // WebPMux* mux = WebPMuxCreate(&data, copy_data); -// WebPMuxGetImage(mux, &image); +// WebPMuxGetFrame(mux, 1, &image); // // ... (Consume image; e.g. call WebPDecode() to decode the data). // WebPMuxGetChunk(mux, "ICCP", &icc_profile); // // ... (Consume icc_data). @@ -151,8 +151,8 @@ WEBP_EXTERN(WebPMux*) WebPMuxCreateInternal(const WebPData*, int, int); // Creates a mux object from raw data given in WebP RIFF format. // Parameters: // bitstream - (in) the bitstream data in WebP RIFF format -// copy_data - (in) value 1 indicates given data WILL copied to the mux, and -// value 0 indicates data will NOT be copied. +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// and value 0 indicates data will NOT be copied. // Returns: // A pointer to the mux object created from given data - on success. // NULL - In case of invalid data or memory error. @@ -162,54 +162,12 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, } //------------------------------------------------------------------------------ -// 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 -// bitstream - (in) can either be a raw VP8/VP8L bitstream or a single-image -// WebP file (non-animated and non-tiled) -// 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 bitstream is NULL. -// WEBP_MUX_MEMORY_ERROR - on memory allocation error. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(WebPMux* mux, - const WebPData* bitstream, - int copy_data); - -// Gets image data from the mux object. -// The content of 'bitstream' is allocated using malloc(), and NOT -// owned by the 'mux' object. It MUST be deallocated by the caller by calling -// WebPDataClear(). -// Parameters: -// mux - (in) object from which the image is to be fetched -// bitstream - (out) the image data -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if either mux or bitstream 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* mux, - WebPData* bitstream); - -// 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* mux); - -//------------------------------------------------------------------------------ -// Chunks. +// Non-image chunks. // Note: Only non-image related chunks should be managed through chunk APIs. // (Image related chunks are: "FRM ", "TILE", "VP8 ", "VP8L" and "ALPH"). +// To add, get and delete images, use APIs WebPMuxSetImage(), +// WebPMuxPushFrame(), WebPMuxGetFrame() and WebPMuxDeleteFrame(). // Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object. // Any existing chunk(s) with the same id will be removed. @@ -218,8 +176,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteImage(WebPMux* mux); // fourcc - (in) a character array containing the fourcc of the given chunk; // e.g., "ICCP", "META" etc. // chunk_data - (in) the chunk data to be added -// copy_data - (in) value 1 indicates given data WILL copied to the mux, and -// value 0 indicates data will NOT be copied. +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// and value 0 indicates data will NOT be copied. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux or chunk_data is NULL // or if fourcc corresponds to an image chunk. @@ -258,26 +216,48 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk( WebPMux* mux, const char fourcc[4]); //------------------------------------------------------------------------------ -// Animation. +// Images. // Encapsulates data about a single frame/tile. struct WebPMuxFrameInfo { - WebPData bitstream_; // image data: can either be a raw VP8/VP8L bitstream - // or a single-image WebP file. - int x_offset_; // x-offset of the frame. - int y_offset_; // y-offset of the frame. - int duration_; // duration of the frame (in milliseconds). - uint32_t pad[3]; // padding for later use + WebPData bitstream_; // image data: can either be a raw VP8/VP8L bitstream + // or a single-image WebP file. + int x_offset_; // x-offset of the frame. + int y_offset_; // y-offset of the frame. + int duration_; // duration of the frame (in milliseconds). + + WebPChunkId id; // frame type: should be one of WEBP_CHUNK_FRAME, + // WEBP_CHUNK_TILE or WEBP_CHUNK_IMAGE + uint32_t pad[3]; // padding for later use }; -// Adds an animation frame at the end of the mux object. -// Note: as WebP only supports even offsets, any odd offset will be snapped to -// an even location using: offset &= ~1 +// Sets the (non-animated and non-tiled) image in the mux object. +// Note: Any existing images (including frames/tiles) will be removed. // Parameters: -// mux - (in/out) object to which an animation frame is to be added +// mux - (in/out) object in which the image is to be set +// bitstream - (in) can either be a raw VP8/VP8L bitstream or a single-image +// WebP file (non-animated and non-tiled) +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// and value 0 indicates data will NOT be copied. +// Returns: +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL. +// WEBP_MUX_MEMORY_ERROR - on memory allocation error. +// WEBP_MUX_OK - on success. +WEBP_EXTERN(WebPMuxError) WebPMuxSetImage( + WebPMux* mux, const WebPData* bitstream, int copy_data); + +// Adds a frame at the end of the mux object. +// Notes: (1) frame.id should be one of WEBP_CHUNK_FRAME or WEBP_CHUNK_TILE +// (2) For setting a non-animated non-tiled image, use WebPMuxSetImage() +// instead. +// (3) Type of frame being pushed must be same as the frames in mux. +// (4) As WebP only supports even offsets, any odd offset will be snapped +// to an even location using: offset &= ~1 +// Parameters: +// mux - (in/out) object to which the frame is to be added // frame - (in) frame data. -// copy_data - (in) value 1 indicates given data WILL copied to the mux, and -// value 0 indicates data will NOT be copied. +// copy_data - (in) value 1 indicates given data WILL be copied to the mux +// and value 0 indicates data will NOT be copied. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL // or if content of 'frame' is invalid. @@ -286,7 +266,7 @@ struct WebPMuxFrameInfo { WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame( WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data); -// Gets the nth animation frame from the mux object. +// Gets the nth frame from the mux object. // The content of 'frame->bitstream_' is allocated using malloc(), and NOT // owned by the 'mux' object. It MUST be deallocated by the caller by calling // WebPDataClear(). @@ -296,25 +276,28 @@ WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame( // nth - (in) index of the frame in the mux object // frame - (out) data of the returned frame // Returns: -// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL +// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL. // WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object. // WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid. // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame( const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame); -// Deletes an animation frame from the mux object. +// Deletes a frame from the mux object. // nth=0 has a special meaning - last position. // Parameters: // mux - (in/out) object from which a frame is to be deleted // nth - (in) The position from which the frame is to be deleted // Returns: -// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL +// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL. // WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object // before deletion. // WEBP_MUX_OK - on success. WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth); +//------------------------------------------------------------------------------ +// Animation. + // Sets the animation loop count in the mux object. Any existing loop count // value(s) will be removed. // Parameters: @@ -338,54 +321,6 @@ WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* mux, int loop_count); WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count); -//------------------------------------------------------------------------------ -// Tiling. - -// Adds a tile at the end of the mux object. -// Note: as WebP only supports even offsets, any odd offset will be snapped to -// an even location using: offset &= ~1 -// Parameters: -// mux - (in/out) object to which a tile is to be added. -// tile - (in) tile 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 or tile is NULL -// or if content of 'tile' is invalid. -// WEBP_MUX_MEMORY_ERROR - on memory allocation error. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxPushTile( - WebPMux* mux, const WebPMuxFrameInfo* tile, int copy_data); - -// Gets the nth tile from the mux object. -// The content of 'tile->bitstream_' is allocated using malloc(), and NOT -// owned by the 'mux' object. It MUST be deallocated by the caller by calling -// WebPDataClear(). -// nth=0 has a special meaning - last position. -// Parameters: -// mux - (in) object from which the info is to be fetched -// nth - (in) index of the tile in the mux object -// tile - (out) data of the returned tile -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if either mux or tile is NULL -// WEBP_MUX_NOT_FOUND - if there are less than nth tiles in the mux object. -// WEBP_MUX_BAD_DATA - if nth tile chunk in mux is invalid. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxGetTile( - const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* tile); - -// Deletes a tile from the mux object. -// nth=0 has a special meaning - last position -// Parameters: -// mux - (in/out) object from which a tile is to be deleted -// nth - (in) The position from which the tile is to be deleted -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL -// WEBP_MUX_NOT_FOUND - If there are less than nth tiles in the mux object -// before deletion. -// WEBP_MUX_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxDeleteTile(WebPMux* mux, uint32_t nth); - //------------------------------------------------------------------------------ // Misc Utilities.