From c975c44ea56bec0d7c98d4ea3b73478fc7e47379 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 23 May 2012 15:01:25 +0530 Subject: [PATCH] Alpha flag fix for lossless. - Make sure alpha flag is set in case of a lossless file with VP8X chunk. The semantic of ALPHA_FLAG changes with this: it means the images contain alpha (rather than ALPH chunk in particular). - Update the mux container spec to add 1-line description of alpha flag. - Rename "HasLosslessImages()" to "MuxHasLosslessImages()", and other similar function renames. - Rename FeatureFlags to WebPFeatureFlags - Elaborated a comment for a special case. - A misc comment fix. Change-Id: If212ccf4338c125b4c71c10bf281a51b3ba7ff45 --- doc/webp-container-spec.txt | 12 ++++++++---- src/mux/muxedit.c | 16 +++++++++++---- src/mux/muxi.h | 9 ++++++--- src/mux/muxinternal.c | 39 ++++++++++++++++++++++++++----------- src/mux/muxread.c | 8 ++++---- src/webp/mux.h | 4 ++-- 6 files changed, 60 insertions(+), 28 deletions(-) diff --git a/doc/webp-container-spec.txt b/doc/webp-container-spec.txt index 85d45a89..2ee4fdc4 100644 --- a/doc/webp-container-spec.txt +++ b/doc/webp-container-spec.txt @@ -13,7 +13,7 @@ end of this file. WebP Container Specification ============================ -_Working Draft, v0.2, 20120207_ +_Working Draft, v0.3, 20120523_ * TOC placeholder @@ -271,7 +271,7 @@ Extended WebP file header: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ChunkHeader('VP8X') | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Rsrv |M|I|A|T| Reserved | + | Rsrv |AL|M|I|A|T| Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Canvas Width | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -295,11 +295,15 @@ Metadata (M): 1 bit : Set if the file contains a 'META' chunk. -Reserved (Rsrv): 4 bits +Alpha (AL): 1 bit + +: Set if the file contains images with transparency information ("alpha"). + +Reserved (Rsrv): 3 bits : SHOULD be `0`. -Reserved: 16 bits +Reserved: 24 bits : SHOULD be `0`. diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index e99aae8c..c876b965 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -218,7 +218,7 @@ static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, if (mux == NULL || name == NULL) return WEBP_MUX_INVALID_ARGUMENT; if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT; - chunk_list = GetChunkListFromId(mux, id); + chunk_list = MuxGetChunkListFromId(mux, id); if (chunk_list == NULL) return WEBP_MUX_INVALID_ARGUMENT; return DeleteChunks(chunk_list, kChunks[idx].tag); @@ -390,7 +390,7 @@ static WebPMuxError MuxAddFrameTileInternal( image_info = NULL; // Owned by 'chunk' now. err = ChunkSetNth(&chunk, &wpi.img_, 1); if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.vp8_ now. + ChunkInit(&chunk); // chunk owned by wpi.img_ now. // Create frame/tile data from image_info. err = CreateDataFromImageInfo(wpi.img_->image_info_, is_frame, @@ -452,7 +452,7 @@ WebPMuxError WebPMuxDeleteImage(WebPMux* const mux) { if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - err = ValidateForImage(mux); + err = MuxValidateForImage(mux); if (err != WEBP_MUX_OK) return err; // All well, delete image. @@ -623,6 +623,14 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { err = GetImageCanvasWidthHeight(mux, flags, &width, &height); if (err != WEBP_MUX_OK) return err; + if (MuxHasLosslessImages(images)) { + // We have a file with a VP8X chunk having some lossless images. + // As lossless images implicitly contain alpha, force ALPHA_FLAG to be true. + // Note: This 'flags' update must NOT be done for a lossless image + // without a VP8X chunk! + flags |= ALPHA_FLAG; + } + PutLE32(data + 0, flags); // VP8X chunk flags. PutLE32(data + 4, width); // canvas width. PutLE32(data + 8, height); // canvas height. @@ -695,7 +703,7 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, assert(dst == data + size); // Validate mux. - err = WebPMuxValidate(mux); + err = MuxValidate(mux); if (err != WEBP_MUX_OK) { free(data); data = NULL; diff --git a/src/mux/muxi.h b/src/mux/muxi.h index 844788fa..bc5f8534 100644 --- a/src/mux/muxi.h +++ b/src/mux/muxi.h @@ -257,19 +257,22 @@ size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list); // Write out the given list of images into 'dst'. uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst); +// Checks if the given image list contains at least one lossless image. +int MuxHasLosslessImages(const WebPMuxImage* images); + //------------------------------------------------------------------------------ // Helper methods for mux. // Returns the list where chunk with given ID is to be inserted in mux. // Return value is NULL if this chunk should be inserted in mux->images_ list // or if 'id' is not known. -WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id); +WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, TAG_ID id); // Validates that the given mux has a single image. -WebPMuxError ValidateForImage(const WebPMux* const mux); +WebPMuxError MuxValidateForImage(const WebPMux* const mux); // Validates the given mux object. -WebPMuxError WebPMuxValidate(const WebPMux* const mux); +WebPMuxError MuxValidate(const WebPMux* const mux); //------------------------------------------------------------------------------ diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 7c55e2ac..4e993e06 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -422,10 +422,21 @@ uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) { return dst; } +int MuxHasLosslessImages(const WebPMuxImage* images) { + while (images != NULL) { + assert(images->img_ != NULL); + if (images->img_->tag_ == kChunks[IDX_VP8L].tag) { + return 1; + } + images = images->next_; + } + return 0; +} + //------------------------------------------------------------------------------ // Helper methods for mux. -WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id) { +WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, TAG_ID id) { assert(mux != NULL); switch(id) { case VP8X_ID: return (WebPChunk**)&mux->vp8x_; @@ -437,7 +448,7 @@ WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id) { } } -WebPMuxError ValidateForImage(const WebPMux* const mux) { +WebPMuxError MuxValidateForImage(const WebPMux* const mux) { const int num_images = MuxImageCount(mux->images_, IMAGE_ID); const int num_frames = MuxImageCount(mux->images_, FRAME_ID); const int num_tiles = MuxImageCount(mux->images_, TILE_ID); @@ -465,7 +476,8 @@ static int IsNotCompatible(int feature, int num_items) { // and feature incompatibility (use NO_FLAG to skip). // On success returns WEBP_MUX_OK and stores the chunk count in *num. static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, - FeatureFlags feature, FeatureFlags vp8x_flags, + WebPFeatureFlags feature, + WebPFeatureFlags vp8x_flags, int max, int* num) { const WebPMuxError err = WebPMuxNumNamedElements(mux, kChunks[idx].name, num); @@ -477,7 +489,7 @@ static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, return WEBP_MUX_OK; } -WebPMuxError WebPMuxValidate(const WebPMux* const mux) { +WebPMuxError MuxValidate(const WebPMux* const mux) { int num_iccp; int num_meta; int num_loop_chunks; @@ -541,14 +553,19 @@ WebPMuxError WebPMuxValidate(const WebPMux* const mux) { if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT; // ALPHA_FLAG & alpha chunk(s) are consistent. - err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha); - if (err != WEBP_MUX_OK) return err; + if (num_vp8x > 0 && MuxHasLosslessImages(mux->images_)) { + // Special case: we have a VP8X chunk as well as some lossless images. + if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT; + } else { + err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha); + if (err != WEBP_MUX_OK) return err; - // num_images & num_alpha_chunks are consistent. - 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; + // num_images & num_alpha_chunks are consistent. + if (num_alpha > 0 && num_alpha != num_images) { + // Note that "num_alpha > 0" is the correct test but "flags && ALPHA_FLAG" + // is NOT, because ALPHA_FLAG is based on first image only. + return WEBP_MUX_INVALID_ARGUMENT; + } } // num_tiles & num_images are consistent. diff --git a/src/mux/muxread.c b/src/mux/muxread.c index 23163213..01c2a275 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -166,7 +166,7 @@ WebPMux* WebPMuxCreateInternal(const uint8_t* data, size_t size, int copy_data, 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. + chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk. if (chunk_list == NULL) chunk_list = &mux->unknown_; if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; } @@ -179,7 +179,7 @@ WebPMux* WebPMuxCreateInternal(const uint8_t* data, size_t size, int copy_data, } // Validate mux if complete. - if (WebPMuxValidate(mux) != WEBP_MUX_OK) goto Err; + if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; Ok: MuxImageDelete(wpi); @@ -234,7 +234,7 @@ WebPMuxError WebPMuxGetImage(const WebPMux* const mux, return WEBP_MUX_INVALID_ARGUMENT; } - err = ValidateForImage(mux); + err = MuxValidateForImage(mux); if (err != WEBP_MUX_OK) return err; // All well. Get the image. @@ -381,7 +381,7 @@ WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* name, if (IsWPI(id)) { *num_elements = MuxImageCount(mux->images_, id); } else { - WebPChunk* const* chunk_list = GetChunkListFromId(mux, id); + WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id); if (chunk_list == NULL) { *num_elements = 0; } else { diff --git a/src/webp/mux.h b/src/webp/mux.h index a5348abb..6125235c 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -78,7 +78,7 @@ typedef enum { ICCP_FLAG = 0x00000004, META_FLAG = 0x00000008, ALPHA_FLAG = 0x00000010 -} FeatureFlags; +} WebPFeatureFlags; typedef struct WebPMux WebPMux; // main opaque object. @@ -430,7 +430,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth); // mux - (in) object from which the features are to be fetched // flags - (out) the flags specifying which features are present in the // mux object. This will be an OR of various flag values. -// Enum 'FeatureFlags' can be used to test for individual flag values. +// Enum 'WebPFeatureFlags' can be used to test individual flag values. // Returns: // WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or flags is NULL // WEBP_MUX_NOT_FOUND - if VP8X chunk is not present in mux object.