mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 12:28:26 +01:00
Merge changes I9b4dc36c,I4e0eef4d
* changes: Mux: support parsing unknown chunks within a frame/fragment. Stricter check for presence of alpha when writing lossless images
This commit is contained in:
commit
11249abfc7
@ -497,8 +497,10 @@ static int WriteWebPWithMetadata(FILE* const out,
|
|||||||
webp_size -= kVP8XChunkSize;
|
webp_size -= kVP8XChunkSize;
|
||||||
} else {
|
} else {
|
||||||
const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
|
const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
|
||||||
// The alpha flag is forced with lossless images.
|
if (is_lossless) {
|
||||||
if (is_lossless) flags |= kAlphaFlag;
|
// Presence of alpha is stored in the 29th bit of VP8L data.
|
||||||
|
if (webp[kChunkHeaderSize + 3] & (1 << 5)) flags |= kAlphaFlag;
|
||||||
|
}
|
||||||
ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
|
ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
|
||||||
ok = ok && WriteLE32(out, flags);
|
ok = ok && WriteLE32(out, flags);
|
||||||
ok = ok && WriteLE24(out, picture->width - 1);
|
ok = ok && WriteLE24(out, picture->width - 1);
|
||||||
|
@ -39,12 +39,6 @@ WebPMux* WebPNewInternal(int version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DeleteAllChunks(WebPChunk** const chunk_list) {
|
|
||||||
while (*chunk_list) {
|
|
||||||
*chunk_list = ChunkDelete(*chunk_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all images in 'wpi_list'.
|
// Delete all images in 'wpi_list'.
|
||||||
static void DeleteAllImages(WebPMuxImage** const wpi_list) {
|
static void DeleteAllImages(WebPMuxImage** const wpi_list) {
|
||||||
while (*wpi_list != NULL) {
|
while (*wpi_list != NULL) {
|
||||||
@ -55,12 +49,12 @@ static void DeleteAllImages(WebPMuxImage** const wpi_list) {
|
|||||||
static void MuxRelease(WebPMux* const mux) {
|
static void MuxRelease(WebPMux* const mux) {
|
||||||
if (mux == NULL) return;
|
if (mux == NULL) return;
|
||||||
DeleteAllImages(&mux->images_);
|
DeleteAllImages(&mux->images_);
|
||||||
DeleteAllChunks(&mux->vp8x_);
|
ChunkListDelete(&mux->vp8x_);
|
||||||
DeleteAllChunks(&mux->iccp_);
|
ChunkListDelete(&mux->iccp_);
|
||||||
DeleteAllChunks(&mux->anim_);
|
ChunkListDelete(&mux->anim_);
|
||||||
DeleteAllChunks(&mux->exif_);
|
ChunkListDelete(&mux->exif_);
|
||||||
DeleteAllChunks(&mux->xmp_);
|
ChunkListDelete(&mux->xmp_);
|
||||||
DeleteAllChunks(&mux->unknown_);
|
ChunkListDelete(&mux->unknown_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebPMuxDelete(WebPMux* mux) {
|
void WebPMuxDelete(WebPMux* mux) {
|
||||||
@ -103,34 +97,26 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth,
|
|||||||
|
|
||||||
// Create data for frame/fragment given image data, offsets and duration.
|
// Create data for frame/fragment given image data, offsets and duration.
|
||||||
static WebPMuxError CreateFrameFragmentData(
|
static WebPMuxError CreateFrameFragmentData(
|
||||||
const WebPData* const image, int x_offset, int y_offset, int duration,
|
int width, int height, const WebPMuxFrameInfo* const info, int is_frame,
|
||||||
WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame,
|
|
||||||
WebPData* const frame_frgm) {
|
WebPData* const frame_frgm) {
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
uint8_t* frame_frgm_bytes;
|
uint8_t* frame_frgm_bytes;
|
||||||
const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;
|
const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;
|
||||||
|
|
||||||
const int ok = is_lossless ?
|
assert(width > 0 && height > 0 && info->duration >= 0);
|
||||||
VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) :
|
assert(info->dispose_method == (info->dispose_method & 1));
|
||||||
VP8GetInfo(image->bytes, image->size, image->size, &width, &height);
|
|
||||||
if (!ok) return WEBP_MUX_INVALID_ARGUMENT;
|
|
||||||
|
|
||||||
assert(width > 0 && height > 0 && duration >= 0);
|
|
||||||
assert(dispose_method == (dispose_method & 1));
|
|
||||||
// Note: assertion on upper bounds is done in PutLE24().
|
// Note: assertion on upper bounds is done in PutLE24().
|
||||||
|
|
||||||
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
|
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
|
||||||
if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
|
if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||||
|
|
||||||
PutLE24(frame_frgm_bytes + 0, x_offset / 2);
|
PutLE24(frame_frgm_bytes + 0, info->x_offset / 2);
|
||||||
PutLE24(frame_frgm_bytes + 3, y_offset / 2);
|
PutLE24(frame_frgm_bytes + 3, info->y_offset / 2);
|
||||||
|
|
||||||
if (is_frame) {
|
if (is_frame) {
|
||||||
PutLE24(frame_frgm_bytes + 6, width - 1);
|
PutLE24(frame_frgm_bytes + 6, width - 1);
|
||||||
PutLE24(frame_frgm_bytes + 9, height - 1);
|
PutLE24(frame_frgm_bytes + 9, height - 1);
|
||||||
PutLE24(frame_frgm_bytes + 12, duration);
|
PutLE24(frame_frgm_bytes + 12, info->duration);
|
||||||
frame_frgm_bytes[15] = (dispose_method & 1);
|
frame_frgm_bytes[15] = (info->dispose_method & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_frgm->bytes = frame_frgm_bytes;
|
frame_frgm->bytes = frame_frgm_bytes;
|
||||||
@ -241,7 +227,9 @@ static WebPMuxError SetAlphaAndImageChunks(
|
|||||||
&wpi->alpha_);
|
&wpi->alpha_);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
}
|
}
|
||||||
return AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_);
|
err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_);
|
||||||
|
if (err != WEBP_MUX_OK) return err;
|
||||||
|
return MuxImageFinalize(wpi) ? WEBP_MUX_OK : WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
|
WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
|
||||||
@ -315,24 +303,24 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame,
|
|||||||
assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful.
|
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 WebPMuxAnimDispose dispose_method =
|
|
||||||
is_frame ? frame->dispose_method : 0 /* unused */;
|
|
||||||
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
|
|
||||||
WebPData frame_frgm;
|
WebPData frame_frgm;
|
||||||
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
|
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
|
||||||
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
|
WebPMuxFrameInfo tmp = *frame;
|
||||||
(duration < 0 || duration >= MAX_DURATION) ||
|
tmp.x_offset &= ~1; // Snap offsets to even.
|
||||||
dispose_method != (dispose_method & 1)) {
|
tmp.y_offset &= ~1;
|
||||||
|
if (!is_frame) { // Reset unused values.
|
||||||
|
tmp.duration = 1;
|
||||||
|
tmp.dispose_method = 0;
|
||||||
|
}
|
||||||
|
if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET ||
|
||||||
|
tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET ||
|
||||||
|
(tmp.duration < 0 || tmp.duration >= MAX_DURATION) ||
|
||||||
|
tmp.dispose_method != (tmp.dispose_method & 1)) {
|
||||||
err = WEBP_MUX_INVALID_ARGUMENT;
|
err = WEBP_MUX_INVALID_ARGUMENT;
|
||||||
goto Err;
|
goto Err;
|
||||||
}
|
}
|
||||||
err = CreateFrameFragmentData(&wpi.img_->data_, x_offset, y_offset,
|
err = CreateFrameFragmentData(wpi.width_, wpi.height_, &tmp, is_frame,
|
||||||
duration, dispose_method, is_lossless,
|
&frame_frgm);
|
||||||
is_frame, &frame_frgm);
|
|
||||||
if (err != WEBP_MUX_OK) goto Err;
|
if (err != WEBP_MUX_OK) goto Err;
|
||||||
// Add frame/fragment chunk (with copy_data = 1).
|
// Add frame/fragment chunk (with copy_data = 1).
|
||||||
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
|
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
|
||||||
@ -407,34 +395,10 @@ static WebPMuxError GetFrameFragmentInfo(
|
|||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError MuxGetImageInfo(const WebPChunk* const image_chunk,
|
|
||||||
int* const width, int* const height,
|
|
||||||
int* const has_alpha) {
|
|
||||||
const uint32_t tag = image_chunk->tag_;
|
|
||||||
const WebPData* const data = &image_chunk->data_;
|
|
||||||
int w, h;
|
|
||||||
int a = 0;
|
|
||||||
int ok;
|
|
||||||
assert(image_chunk != NULL);
|
|
||||||
assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag);
|
|
||||||
ok = (tag == kChunks[IDX_VP8].tag) ?
|
|
||||||
VP8GetInfo(data->bytes, data->size, data->size, &w, &h) :
|
|
||||||
VP8LGetInfo(data->bytes, data->size, &w, &h, &a);
|
|
||||||
if (ok) {
|
|
||||||
if (width != NULL) *width = w;
|
|
||||||
if (height != NULL) *height = h;
|
|
||||||
if (has_alpha != NULL) *has_alpha = a;
|
|
||||||
return WEBP_MUX_OK;
|
|
||||||
} else {
|
|
||||||
return WEBP_MUX_BAD_DATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
|
static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
|
||||||
int* const x_offset, int* const y_offset,
|
int* const x_offset, int* const y_offset,
|
||||||
int* const duration,
|
int* const duration,
|
||||||
int* const width, int* const height) {
|
int* const width, int* const height) {
|
||||||
const WebPChunk* const image_chunk = wpi->img_;
|
|
||||||
const WebPChunk* const frame_frgm_chunk = wpi->header_;
|
const WebPChunk* const frame_frgm_chunk = wpi->header_;
|
||||||
|
|
||||||
// Get offsets and duration from ANMF/FRGM chunk.
|
// Get offsets and duration from ANMF/FRGM chunk.
|
||||||
@ -443,7 +407,9 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
|
|||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
|
|
||||||
// Get width and height from VP8/VP8L chunk.
|
// Get width and height from VP8/VP8L chunk.
|
||||||
return MuxGetImageInfo(image_chunk, width, height, NULL);
|
if (width != NULL) *width = wpi->width_;
|
||||||
|
if (height != NULL) *height = wpi->height_;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebPMuxError GetImageCanvasWidthHeight(
|
static WebPMuxError GetImageCanvasWidthHeight(
|
||||||
@ -488,13 +454,9 @@ static WebPMuxError GetImageCanvasWidthHeight(
|
|||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For a single image, extract the width & height from VP8/VP8L image-data.
|
// For a single image, canvas dimensions are same as image dimensions.
|
||||||
int w, h;
|
*width = wpi->width_;
|
||||||
const WebPChunk* const image_chunk = wpi->img_;
|
*height = wpi->height_;
|
||||||
const WebPMuxError err = MuxGetImageInfo(image_chunk, &w, &h, NULL);
|
|
||||||
if (err != WEBP_MUX_OK) return err;
|
|
||||||
*width = w;
|
|
||||||
*height = h;
|
|
||||||
}
|
}
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
@ -563,9 +525,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
|||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MuxHasLosslessImages(images)) {
|
if (MuxHasAlpha(images)) {
|
||||||
// We have a file with a VP8X chunk having some lossless images.
|
// This means some frames explicitly/implicitly contain alpha.
|
||||||
// As lossless images implicitly contain alpha, force ALPHA_FLAG to be true.
|
|
||||||
// Note: This 'flags' update must NOT be done for a lossless image
|
// Note: This 'flags' update must NOT be done for a lossless image
|
||||||
// without a VP8X chunk!
|
// without a VP8X chunk!
|
||||||
flags |= ALPHA_FLAG;
|
flags |= ALPHA_FLAG;
|
||||||
@ -614,16 +575,6 @@ static WebPMuxError MuxCleanup(WebPMux* const mux) {
|
|||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total size of a list of chunks.
|
|
||||||
static size_t ChunkListDiskSize(const WebPChunk* chunk_list) {
|
|
||||||
size_t size = 0;
|
|
||||||
while (chunk_list != NULL) {
|
|
||||||
size += ChunkDiskSize(chunk_list);
|
|
||||||
chunk_list = chunk_list->next_;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Total size of a list of images.
|
// Total size of a list of images.
|
||||||
static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) {
|
static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
@ -48,6 +48,10 @@ struct WebPMuxImage {
|
|||||||
WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF/WEBP_CHUNK_FRGM.
|
WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF/WEBP_CHUNK_FRGM.
|
||||||
WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA.
|
WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA.
|
||||||
WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE.
|
WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE.
|
||||||
|
WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN.
|
||||||
|
int width_;
|
||||||
|
int height_;
|
||||||
|
int has_alpha_; // Through ALPH chunk or as part of VP8L.
|
||||||
int is_partial_; // True if only some of the chunks are filled.
|
int is_partial_; // True if only some of the chunks are filled.
|
||||||
WebPMuxImage* next_;
|
WebPMuxImage* next_;
|
||||||
};
|
};
|
||||||
@ -133,6 +137,9 @@ WebPChunk* ChunkRelease(WebPChunk* const chunk);
|
|||||||
// Deletes given chunk & returns chunk->next_.
|
// Deletes given chunk & returns chunk->next_.
|
||||||
WebPChunk* ChunkDelete(WebPChunk* const chunk);
|
WebPChunk* ChunkDelete(WebPChunk* const chunk);
|
||||||
|
|
||||||
|
// Deletes all chunks in the given chunk list.
|
||||||
|
void ChunkListDelete(WebPChunk** const chunk_list);
|
||||||
|
|
||||||
// Returns size of the chunk including chunk header and padding byte (if any).
|
// Returns size of the chunk including chunk header and padding byte (if any).
|
||||||
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
||||||
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
||||||
@ -145,14 +152,12 @@ static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
|
|||||||
return SizeWithPadding(data_size);
|
return SizeWithPadding(data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Total size of a list of chunks.
|
||||||
|
size_t ChunkListDiskSize(const WebPChunk* chunk_list);
|
||||||
|
|
||||||
// Write out the given list of chunks into 'dst'.
|
// Write out the given list of chunks into 'dst'.
|
||||||
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
|
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
|
||||||
|
|
||||||
// Get the width, height and has_alpha info of image stored in 'image_chunk'.
|
|
||||||
WebPMuxError MuxGetImageInfo(const WebPChunk* const image_chunk,
|
|
||||||
int* const width, int* const height,
|
|
||||||
int* const has_alpha);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// MuxImage object management.
|
// MuxImage object management.
|
||||||
|
|
||||||
@ -170,6 +175,10 @@ WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi);
|
|||||||
// If id == WEBP_CHUNK_NIL, all images will be matched.
|
// If id == WEBP_CHUNK_NIL, all images will be matched.
|
||||||
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id);
|
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id);
|
||||||
|
|
||||||
|
// Update width/height/has_alpha info from chunks within wpi.
|
||||||
|
// Also remove ALPH chunk if not needed.
|
||||||
|
int MuxImageFinalize(WebPMuxImage* const wpi);
|
||||||
|
|
||||||
// Check if given ID corresponds to an image related chunk.
|
// Check if given ID corresponds to an image related chunk.
|
||||||
static WEBP_INLINE int IsWPI(WebPChunkId id) {
|
static WEBP_INLINE int IsWPI(WebPChunkId id) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
@ -200,8 +209,8 @@ uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst);
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Helper methods for mux.
|
// Helper methods for mux.
|
||||||
|
|
||||||
// Checks if the given image list contains at least one lossless image.
|
// Checks if the given image list contains at least one image with alpha.
|
||||||
int MuxHasLosslessImages(const WebPMuxImage* images);
|
int MuxHasAlpha(const WebPMuxImage* images);
|
||||||
|
|
||||||
// Write out RIFF header into 'data', given total data size 'size'.
|
// Write out RIFF header into 'data', given total data size 'size'.
|
||||||
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
|
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
|
||||||
|
@ -187,6 +187,12 @@ WebPChunk* ChunkDelete(WebPChunk* const chunk) {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChunkListDelete(WebPChunk** const chunk_list) {
|
||||||
|
while (*chunk_list != NULL) {
|
||||||
|
*chunk_list = ChunkDelete(*chunk_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Chunk serialization methods.
|
// Chunk serialization methods.
|
||||||
|
|
||||||
@ -211,6 +217,15 @@ uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t ChunkListDiskSize(const WebPChunk* chunk_list) {
|
||||||
|
size_t size = 0;
|
||||||
|
while (chunk_list != NULL) {
|
||||||
|
size += ChunkDiskSize(chunk_list);
|
||||||
|
chunk_list = chunk_list->next_;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Life of a MuxImage object.
|
// Life of a MuxImage object.
|
||||||
|
|
||||||
@ -225,6 +240,7 @@ WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
|
|||||||
ChunkDelete(wpi->header_);
|
ChunkDelete(wpi->header_);
|
||||||
ChunkDelete(wpi->alpha_);
|
ChunkDelete(wpi->alpha_);
|
||||||
ChunkDelete(wpi->img_);
|
ChunkDelete(wpi->img_);
|
||||||
|
ChunkListDelete(&wpi->unknown_);
|
||||||
|
|
||||||
next = wpi->next_;
|
next = wpi->next_;
|
||||||
MuxImageInit(wpi);
|
MuxImageInit(wpi);
|
||||||
@ -356,6 +372,7 @@ size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
|
|||||||
if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
|
if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
|
||||||
if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
|
if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
|
||||||
if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_);
|
if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_);
|
||||||
|
if (wpi->unknown_ != NULL) size += ChunkListDiskSize(wpi->unknown_);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,18 +404,16 @@ uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
|
|||||||
}
|
}
|
||||||
if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
|
if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
|
||||||
if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
|
if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
|
||||||
|
if (wpi->unknown_ != NULL) dst = ChunkListEmit(wpi->unknown_, dst);
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Helper methods for mux.
|
// Helper methods for mux.
|
||||||
|
|
||||||
int MuxHasLosslessImages(const WebPMuxImage* images) {
|
int MuxHasAlpha(const WebPMuxImage* images) {
|
||||||
while (images != NULL) {
|
while (images != NULL) {
|
||||||
assert(images->img_ != NULL);
|
if (images->has_alpha_) return 1;
|
||||||
if (images->img_->tag_ == kChunks[IDX_VP8L].tag) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
images = images->next_;
|
images = images->next_;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -512,14 +527,18 @@ WebPMuxError MuxValidate(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 (MuxHasLosslessImages(mux->images_)) {
|
if (MuxHasAlpha(mux->images_)) {
|
||||||
if (num_vp8x > 0) {
|
if (num_vp8x > 0) {
|
||||||
// Special case: we have a VP8X chunk as well as some lossless images.
|
// VP8X chunk is present, so it should contain ALPHA_FLAG.
|
||||||
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
|
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
} else {
|
||||||
} else {
|
// VP8X chunk is not present, so ALPH chunks should NOT be present either.
|
||||||
err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha);
|
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
|
if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
} else { // Mux doesn't need alpha. So, ALPHA_FLAG should NOT be present.
|
||||||
|
if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// num_fragments & num_images are consistent.
|
// num_fragments & num_images are consistent.
|
||||||
|
@ -76,6 +76,29 @@ static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
|
|||||||
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
|
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MuxImageFinalize(WebPMuxImage* const wpi) {
|
||||||
|
const WebPChunk* const img = wpi->img_;
|
||||||
|
const WebPData* const image = &img->data_;
|
||||||
|
const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
|
||||||
|
int w, h;
|
||||||
|
int vp8l_has_alpha = 0;
|
||||||
|
const int ok = is_lossless ?
|
||||||
|
VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
|
||||||
|
VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
|
||||||
|
assert(img != NULL);
|
||||||
|
if (ok) {
|
||||||
|
// Ignore ALPH chunk accompanying VP8L.
|
||||||
|
if (is_lossless && (wpi->alpha_ != NULL)) {
|
||||||
|
ChunkDelete(wpi->alpha_);
|
||||||
|
wpi->alpha_ = NULL;
|
||||||
|
}
|
||||||
|
wpi->width_ = w;
|
||||||
|
wpi->height_ = h;
|
||||||
|
wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
||||||
WebPMuxImage* const wpi) {
|
WebPMuxImage* const wpi) {
|
||||||
const uint8_t* bytes = chunk->data_.bytes;
|
const uint8_t* bytes = chunk->data_.bytes;
|
||||||
@ -121,8 +144,14 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
|||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_IMAGE:
|
case WEBP_CHUNK_IMAGE:
|
||||||
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
||||||
|
if (!MuxImageFinalize(wpi)) goto Fail;
|
||||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||||
break;
|
break;
|
||||||
|
case WEBP_CHUNK_UNKNOWN:
|
||||||
|
if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
|
||||||
|
// before some image chunks.
|
||||||
|
if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
goto Fail;
|
goto Fail;
|
||||||
break;
|
break;
|
||||||
@ -218,6 +247,7 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
|||||||
break;
|
break;
|
||||||
case WEBP_CHUNK_IMAGE:
|
case WEBP_CHUNK_IMAGE:
|
||||||
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
|
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
|
||||||
|
if (!MuxImageFinalize(wpi)) goto Err;
|
||||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||||
PushImage:
|
PushImage:
|
||||||
// Add this to mux->images_ list.
|
// Add this to mux->images_ list.
|
||||||
@ -291,16 +321,17 @@ static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
|
|||||||
// Check if VP8X chunk is present.
|
// Check if VP8X chunk is present.
|
||||||
if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
|
if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
|
||||||
if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
|
if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
|
||||||
f = GetLE32(data.bytes);
|
f = GetLE32(data.bytes + 0);
|
||||||
w = GetLE24(data.bytes + 4) + 1;
|
w = GetLE24(data.bytes + 4) + 1;
|
||||||
h = GetLE24(data.bytes + 7) + 1;
|
h = GetLE24(data.bytes + 7) + 1;
|
||||||
} else { // Single image case.
|
} else { // Single image case.
|
||||||
int has_alpha;
|
const WebPMuxImage* const wpi = mux->images_;
|
||||||
WebPMuxError err = ValidateForSingleImage(mux);
|
WebPMuxError err = ValidateForSingleImage(mux);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
if (err != WEBP_MUX_OK) return err;
|
||||||
err = MuxGetImageInfo(mux->images_->img_, &w, &h, &has_alpha);
|
assert(wpi != NULL);
|
||||||
if (err != WEBP_MUX_OK) return err;
|
w = wpi->width_;
|
||||||
if (has_alpha) f |= ALPHA_FLAG;
|
h = wpi->height_;
|
||||||
|
if (wpi->has_alpha_) f |= ALPHA_FLAG;
|
||||||
}
|
}
|
||||||
if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
|
if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
|
||||||
|
|
||||||
@ -355,15 +386,7 @@ static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi,
|
|||||||
dst = MuxEmitRiffHeader(data, size);
|
dst = MuxEmitRiffHeader(data, size);
|
||||||
|
|
||||||
if (need_vp8x) {
|
if (need_vp8x) {
|
||||||
int w, h;
|
dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X.
|
||||||
WebPMuxError err;
|
|
||||||
assert(wpi->img_ != NULL);
|
|
||||||
err = MuxGetImageInfo(wpi->img_, &w, &h, NULL);
|
|
||||||
if (err != WEBP_MUX_OK) {
|
|
||||||
free(data);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
dst = EmitVP8XChunk(dst, w, h, ALPHA_FLAG); // VP8X.
|
|
||||||
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
|
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user