mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-14 21:09:55 +02:00
Stricter check for presence of alpha when writing lossless images
Earlier, all lossless images were assumed to contain alpha. Now, we use the 'alpha_is_used' bit from the VP8L bitstream to determine the same. Detecting an absence of alpha can sometimes lead to much more efficient rendering, especially for animated images. Related: refine mux code to read width/height/has_alpha information only once per frame/fragment. This avoid frequent calls to VP8(L)GetInfo(). Change-Id: I4e0eef4db7d94425396c7dff6ca5599d5bca8297
This commit is contained in:
@ -103,34 +103,26 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth,
|
||||
|
||||
// Create data for frame/fragment given image data, offsets and duration.
|
||||
static WebPMuxError CreateFrameFragmentData(
|
||||
const WebPData* const image, int x_offset, int y_offset, int duration,
|
||||
WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame,
|
||||
int width, int height, const WebPMuxFrameInfo* const info, int is_frame,
|
||||
WebPData* const frame_frgm) {
|
||||
int width;
|
||||
int height;
|
||||
uint8_t* frame_frgm_bytes;
|
||||
const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;
|
||||
|
||||
const int ok = is_lossless ?
|
||||
VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) :
|
||||
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));
|
||||
assert(width > 0 && height > 0 && info->duration >= 0);
|
||||
assert(info->dispose_method == (info->dispose_method & 1));
|
||||
// Note: assertion on upper bounds is done in PutLE24().
|
||||
|
||||
frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
|
||||
if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
|
||||
PutLE24(frame_frgm_bytes + 0, x_offset / 2);
|
||||
PutLE24(frame_frgm_bytes + 3, y_offset / 2);
|
||||
PutLE24(frame_frgm_bytes + 0, info->x_offset / 2);
|
||||
PutLE24(frame_frgm_bytes + 3, info->y_offset / 2);
|
||||
|
||||
if (is_frame) {
|
||||
PutLE24(frame_frgm_bytes + 6, width - 1);
|
||||
PutLE24(frame_frgm_bytes + 9, height - 1);
|
||||
PutLE24(frame_frgm_bytes + 12, duration);
|
||||
frame_frgm_bytes[15] = (dispose_method & 1);
|
||||
PutLE24(frame_frgm_bytes + 12, info->duration);
|
||||
frame_frgm_bytes[15] = (info->dispose_method & 1);
|
||||
}
|
||||
|
||||
frame_frgm->bytes = frame_frgm_bytes;
|
||||
@ -243,7 +235,9 @@ static WebPMuxError SetAlphaAndImageChunks(
|
||||
&wpi->alpha_);
|
||||
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,
|
||||
@ -317,24 +311,24 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame,
|
||||
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;
|
||||
if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET ||
|
||||
y_offset < 0 || y_offset >= MAX_POSITION_OFFSET ||
|
||||
(duration < 0 || duration >= MAX_DURATION) ||
|
||||
dispose_method != (dispose_method & 1)) {
|
||||
const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag;
|
||||
WebPMuxFrameInfo tmp = *frame;
|
||||
tmp.x_offset &= ~1; // Snap offsets to even.
|
||||
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;
|
||||
goto Err;
|
||||
}
|
||||
err = CreateFrameFragmentData(&wpi.img_->data_, x_offset, y_offset,
|
||||
duration, dispose_method, is_lossless,
|
||||
is_frame, &frame_frgm);
|
||||
err = CreateFrameFragmentData(wpi.width_, wpi.height_, &tmp, is_frame,
|
||||
&frame_frgm);
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
// Add frame/fragment chunk (with copy_data = 1).
|
||||
err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_);
|
||||
@ -409,34 +403,10 @@ static WebPMuxError GetFrameFragmentInfo(
|
||||
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,
|
||||
int* const x_offset, int* const y_offset,
|
||||
int* const duration,
|
||||
int* const width, int* const height) {
|
||||
const WebPChunk* const image_chunk = wpi->img_;
|
||||
const WebPChunk* const frame_frgm_chunk = wpi->header_;
|
||||
|
||||
// Get offsets and duration from ANMF/FRGM chunk.
|
||||
@ -445,7 +415,9 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// 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(
|
||||
@ -490,13 +462,9 @@ static WebPMuxError GetImageCanvasWidthHeight(
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
} else {
|
||||
// For a single image, extract the width & height from VP8/VP8L image-data.
|
||||
int w, h;
|
||||
const WebPChunk* const image_chunk = wpi->img_;
|
||||
const WebPMuxError err = MuxGetImageInfo(image_chunk, &w, &h, NULL);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
*width = w;
|
||||
*height = h;
|
||||
// For a single image, canvas dimensions are same as image dimensions.
|
||||
*width = wpi->width_;
|
||||
*height = wpi->height_;
|
||||
}
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
@ -565,9 +533,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
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.
|
||||
if (MuxHasAlpha(images)) {
|
||||
// This means some frames explicitly/implicitly contain alpha.
|
||||
// Note: This 'flags' update must NOT be done for a lossless image
|
||||
// without a VP8X chunk!
|
||||
flags |= ALPHA_FLAG;
|
||||
|
Reference in New Issue
Block a user