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
This commit is contained in:
Urvang Joshi 2012-05-23 15:01:25 +05:30
parent 98ec717f1e
commit c975c44ea5
6 changed files with 60 additions and 28 deletions

View File

@ -13,7 +13,7 @@ end of this file.
WebP Container Specification WebP Container Specification
============================ ============================
_Working Draft, v0.2, 20120207_ _Working Draft, v0.3, 20120523_
* TOC placeholder * TOC placeholder
@ -271,7 +271,7 @@ Extended WebP file header:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') | | ChunkHeader('VP8X') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Rsrv |M|I|A|T| Reserved | | Rsrv |AL|M|I|A|T| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Canvas Width | | Canvas Width |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -295,11 +295,15 @@ Metadata (M): 1 bit
: Set if the file contains a 'META' chunk. : 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`. : SHOULD be `0`.
Reserved: 16 bits Reserved: 24 bits
: SHOULD be `0`. : SHOULD be `0`.

View File

@ -218,7 +218,7 @@ static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux,
if (mux == NULL || name == NULL) return WEBP_MUX_INVALID_ARGUMENT; if (mux == NULL || name == NULL) return WEBP_MUX_INVALID_ARGUMENT;
if (IsWPI(id)) 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; if (chunk_list == NULL) return WEBP_MUX_INVALID_ARGUMENT;
return DeleteChunks(chunk_list, kChunks[idx].tag); return DeleteChunks(chunk_list, kChunks[idx].tag);
@ -390,7 +390,7 @@ static WebPMuxError MuxAddFrameTileInternal(
image_info = NULL; // Owned by 'chunk' now. image_info = NULL; // Owned by 'chunk' now.
err = ChunkSetNth(&chunk, &wpi.img_, 1); err = ChunkSetNth(&chunk, &wpi.img_, 1);
if (err != WEBP_MUX_OK) goto Err; 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. // Create frame/tile data from image_info.
err = CreateDataFromImageInfo(wpi.img_->image_info_, is_frame, 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; if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
err = ValidateForImage(mux); err = MuxValidateForImage(mux);
if (err != WEBP_MUX_OK) return err; if (err != WEBP_MUX_OK) return err;
// All well, delete image. // All well, delete image.
@ -623,6 +623,14 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
err = GetImageCanvasWidthHeight(mux, flags, &width, &height); err = GetImageCanvasWidthHeight(mux, flags, &width, &height);
if (err != WEBP_MUX_OK) return err; 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 + 0, flags); // VP8X chunk flags.
PutLE32(data + 4, width); // canvas width. PutLE32(data + 4, width); // canvas width.
PutLE32(data + 8, height); // canvas height. PutLE32(data + 8, height); // canvas height.
@ -695,7 +703,7 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux,
assert(dst == data + size); assert(dst == data + size);
// Validate mux. // Validate mux.
err = WebPMuxValidate(mux); err = MuxValidate(mux);
if (err != WEBP_MUX_OK) { if (err != WEBP_MUX_OK) {
free(data); free(data);
data = NULL; data = NULL;

View File

@ -257,19 +257,22 @@ size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list);
// Write out the given list of images into 'dst'. // Write out the given list of images into 'dst'.
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* 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. // Helper methods for mux.
// Returns the list where chunk with given ID is to be inserted in 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 // Return value is NULL if this chunk should be inserted in mux->images_ list
// or if 'id' is not known. // 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. // 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. // Validates the given mux object.
WebPMuxError WebPMuxValidate(const WebPMux* const mux); WebPMuxError MuxValidate(const WebPMux* const mux);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -422,10 +422,21 @@ uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
return 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. // Helper methods for mux.
WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id) { WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, TAG_ID id) {
assert(mux != NULL); assert(mux != NULL);
switch(id) { switch(id) {
case VP8X_ID: return (WebPChunk**)&mux->vp8x_; 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_images = MuxImageCount(mux->images_, IMAGE_ID);
const int num_frames = MuxImageCount(mux->images_, FRAME_ID); const int num_frames = MuxImageCount(mux->images_, FRAME_ID);
const int num_tiles = MuxImageCount(mux->images_, TILE_ID); const int num_tiles = MuxImageCount(mux->images_, TILE_ID);
@ -465,7 +476,8 @@ static int IsNotCompatible(int feature, int num_items) {
// and feature incompatibility (use NO_FLAG to skip). // and feature incompatibility (use NO_FLAG to skip).
// On success returns WEBP_MUX_OK and stores the chunk count in *num. // On success returns WEBP_MUX_OK and stores the chunk count in *num.
static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx, static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
FeatureFlags feature, FeatureFlags vp8x_flags, WebPFeatureFlags feature,
WebPFeatureFlags vp8x_flags,
int max, int* num) { int max, int* num) {
const WebPMuxError err = const WebPMuxError err =
WebPMuxNumNamedElements(mux, kChunks[idx].name, num); WebPMuxNumNamedElements(mux, kChunks[idx].name, num);
@ -477,7 +489,7 @@ static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
return WEBP_MUX_OK; return WEBP_MUX_OK;
} }
WebPMuxError WebPMuxValidate(const WebPMux* const mux) { WebPMuxError MuxValidate(const WebPMux* const mux) {
int num_iccp; int num_iccp;
int num_meta; int num_meta;
int num_loop_chunks; int num_loop_chunks;
@ -541,15 +553,20 @@ WebPMuxError WebPMuxValidate(const WebPMux* const mux) {
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT; if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
// ALPHA_FLAG & alpha chunk(s) are consistent. // ALPHA_FLAG & alpha chunk(s) are consistent.
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); err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha);
if (err != WEBP_MUX_OK) return err; if (err != WEBP_MUX_OK) return err;
// num_images & num_alpha_chunks are consistent. // num_images & num_alpha_chunks are consistent.
if (num_alpha > 0 && num_alpha != num_images) { if (num_alpha > 0 && num_alpha != num_images) {
// Note that "num_alpha > 0" is the correct check but "flags && ALPHA_FLAG" // Note that "num_alpha > 0" is the correct test but "flags && ALPHA_FLAG"
// is NOT, because ALPHA_FLAG is based on first image only. // is NOT, because ALPHA_FLAG is based on first image only.
return WEBP_MUX_INVALID_ARGUMENT; return WEBP_MUX_INVALID_ARGUMENT;
} }
}
// num_tiles & num_images are consistent. // num_tiles & num_images are consistent.
if (num_tiles > 0 && num_images != num_tiles) { if (num_tiles > 0 && num_images != num_tiles) {

View File

@ -166,7 +166,7 @@ WebPMux* WebPMuxCreateInternal(const uint8_t* data, size_t size, int copy_data,
WebPChunk** chunk_list; WebPChunk** chunk_list;
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image. // 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 (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; 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. // Validate mux if complete.
if (WebPMuxValidate(mux) != WEBP_MUX_OK) goto Err; if (MuxValidate(mux) != WEBP_MUX_OK) goto Err;
Ok: Ok:
MuxImageDelete(wpi); MuxImageDelete(wpi);
@ -234,7 +234,7 @@ WebPMuxError WebPMuxGetImage(const WebPMux* const mux,
return WEBP_MUX_INVALID_ARGUMENT; return WEBP_MUX_INVALID_ARGUMENT;
} }
err = ValidateForImage(mux); err = MuxValidateForImage(mux);
if (err != WEBP_MUX_OK) return err; if (err != WEBP_MUX_OK) return err;
// All well. Get the image. // All well. Get the image.
@ -381,7 +381,7 @@ WebPMuxError WebPMuxNumNamedElements(const WebPMux* const mux, const char* name,
if (IsWPI(id)) { if (IsWPI(id)) {
*num_elements = MuxImageCount(mux->images_, id); *num_elements = MuxImageCount(mux->images_, id);
} else { } else {
WebPChunk* const* chunk_list = GetChunkListFromId(mux, id); WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
if (chunk_list == NULL) { if (chunk_list == NULL) {
*num_elements = 0; *num_elements = 0;
} else { } else {

View File

@ -78,7 +78,7 @@ typedef enum {
ICCP_FLAG = 0x00000004, ICCP_FLAG = 0x00000004,
META_FLAG = 0x00000008, META_FLAG = 0x00000008,
ALPHA_FLAG = 0x00000010 ALPHA_FLAG = 0x00000010
} FeatureFlags; } WebPFeatureFlags;
typedef struct WebPMux WebPMux; // main opaque object. 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 // mux - (in) object from which the features are to be fetched
// flags - (out) the flags specifying which features are present in the // flags - (out) the flags specifying which features are present in the
// mux object. This will be an OR of various flag values. // 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: // Returns:
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or flags is NULL // 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. // WEBP_MUX_NOT_FOUND - if VP8X chunk is not present in mux object.