mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-13 14:34:33 +02:00
Merge "add colorspace for premultiplied alpha"
This commit is contained in:
@ -22,7 +22,10 @@ extern "C" {
|
||||
// WebPDecBuffer
|
||||
|
||||
// Number of bytes per pixel for the different color-spaces.
|
||||
static const int kModeBpp[MODE_LAST] = { 3, 4, 3, 4, 4, 2, 2, 1, 1 };
|
||||
static const int kModeBpp[MODE_LAST] = {
|
||||
3, 4, 3, 4, 4, 2, 2,
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1 };
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
// Convert to an integer to handle both the unsigned/signed enum cases
|
||||
@ -38,7 +41,7 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
const int height = buffer->height;
|
||||
if (!IsValidColorspace(mode)) {
|
||||
ok = 0;
|
||||
} else if (mode >= MODE_YUV) { // YUV checks
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const size_t size = buf->y_stride * height;
|
||||
const size_t u_size = buf->u_stride * ((height + 1) / 2);
|
||||
@ -81,7 +84,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
const int stride = w * kModeBpp[mode];
|
||||
const uint64_t size = (uint64_t)stride * h;
|
||||
|
||||
if (mode >= MODE_YUV) {
|
||||
if (!WebPIsRGBMode(mode)) {
|
||||
uv_stride = (w + 1) / 2;
|
||||
uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
|
||||
if (mode == MODE_YUVA) {
|
||||
@ -101,7 +104,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (mode >= MODE_YUV) { // YUVA initialization
|
||||
if (!WebPIsRGBMode(mode)) { // YUVA initialization
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
buf->y = output;
|
||||
buf->y_stride = stride;
|
||||
|
@ -200,7 +200,12 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
}
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ && y_start < y_end) {
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
// TODO(skal): several things to correct here:
|
||||
// * testing presence of alpha with dec->alpha_data_ is not a good idea
|
||||
// * we're actually decompressing the full plane only once. It should be
|
||||
// more obvious from signature.
|
||||
// * we could free alpha_data_ right after this call, but we don't own.
|
||||
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
@ -214,7 +219,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
io->y += dec->cache_y_stride_ * delta_y;
|
||||
io->u += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
io->v += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
if (io->a) {
|
||||
if (io->a != NULL) {
|
||||
io->a += io->width * delta_y;
|
||||
}
|
||||
}
|
||||
@ -222,7 +227,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
io->y += io->crop_left;
|
||||
io->u += io->crop_left >> 1;
|
||||
io->v += io->crop_left >> 1;
|
||||
if (io->a) {
|
||||
if (io->a != NULL) {
|
||||
io->a += io->crop_left;
|
||||
}
|
||||
io->mb_y = y_start - io->crop_top;
|
||||
@ -417,7 +422,8 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
const size_t alpha_size =
|
||||
dec->alpha_data_ ? (dec->pic_hdr_.width_ * dec->pic_hdr_.height_) : 0;
|
||||
(dec->alpha_data_ != NULL) ? dec->pic_hdr_.width_ * dec->pic_hdr_.height_
|
||||
: 0;
|
||||
const size_t needed = intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + coeffs_size
|
||||
|
161
src/dec/io.c
161
src/dec/io.c
@ -52,9 +52,7 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* y_src = io->y;
|
||||
const uint8_t* u_src = io->u;
|
||||
const uint8_t* v_src = io->v;
|
||||
const WebPSampleLinePairFunc sample =
|
||||
io->a ? WebPSamplersKeepAlpha[output->colorspace]
|
||||
: WebPSamplers[output->colorspace];
|
||||
const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h - 1;
|
||||
int j;
|
||||
@ -106,9 +104,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_lines_out = io->mb_h; // a priori guess
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
const WebPUpsampleLinePairFunc upsample =
|
||||
io->a ? WebPUpsamplersKeepAlpha[p->output->colorspace]
|
||||
: WebPUpsamplers[p->output->colorspace];
|
||||
WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
|
||||
const uint8_t* cur_y = io->y;
|
||||
const uint8_t* cur_u = io->u;
|
||||
const uint8_t* cur_v = io->v;
|
||||
@ -124,8 +120,6 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
// Warning! Don't overwrite the alpha values (if any), as they
|
||||
// are not lagging one line behind but are already written.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
++num_lines_out;
|
||||
@ -184,45 +178,73 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
}
|
||||
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride +
|
||||
(p->output->colorspace == MODE_ARGB ? 0 : 3);
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
dst[4 * i] = alpha[i];
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int start_y = io->mb_y;
|
||||
int num_rows = mb_h;
|
||||
|
||||
// We compensate for the 1-line delay of fancy upscaler.
|
||||
// This is similar to EmitFancyRGB().
|
||||
if (io->fancy_upsampling) {
|
||||
if (start_y == 0) {
|
||||
// We don't process the last row yet. It'll be done during next call.
|
||||
--num_rows;
|
||||
} else {
|
||||
--start_y;
|
||||
// Fortunately, *alpha data is persistent, so we can go back
|
||||
// one row and finish alpha blending, now that the fancy upscaler
|
||||
// completed the YUV->RGB interpolation.
|
||||
alpha -= io->width;
|
||||
}
|
||||
if (io->crop_top + io->mb_y + mb_h == io->crop_bottom) {
|
||||
// If it's the very last call, we process all the remaing rows!
|
||||
num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||
}
|
||||
}
|
||||
{
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) dst[4 * i] = alpha[i];
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
if (WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t clip(uint32_t v, uint32_t max_value) {
|
||||
return (v > max_value) ? max_value : v;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride + 1;
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha) {
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int i, j;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + io->mb_y * buf->stride;
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_val = clip((alpha[i] + 8) >> 4, 15);
|
||||
dst[2 * i] = (dst[2 * i] & 0xf0) | alpha_val;
|
||||
const uint32_t alpha_val = VP8Clip4Bits(alpha[i]);
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
||||
}
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
if (p->output->colorspace == MODE_rgbA_4444) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, mb_h, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -259,13 +281,8 @@ static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int IsAlphaMode(WEBP_CSP_MODE mode) {
|
||||
return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
|
||||
mode == MODE_RGBA_4444 || mode == MODE_YUVA);
|
||||
}
|
||||
|
||||
static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = IsAlphaMode(p->output->colorspace);
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
@ -302,6 +319,7 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
uv_in_height, uv_out_height,
|
||||
work + work_size + uv_work_size);
|
||||
p->emit = EmitRescaledYUV;
|
||||
|
||||
if (has_alpha) {
|
||||
WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
|
||||
buf->a, out_width, out_height, buf->a_stride, 1,
|
||||
@ -364,59 +382,73 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride +
|
||||
(p->output->colorspace == MODE_ARGB ? 0 : 3);
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
const int width = p->scaler_a.dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < p->scaler_a.dst_width; ++i) {
|
||||
dst[4 * i] = p->scaler_a.dst[i];
|
||||
}
|
||||
for (i = 0; i < width; ++i) dst[4 * i] = p->scaler_a.dst[i];
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride + 1;
|
||||
uint8_t* base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
int num_lines_out = 0;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < p->scaler_a.dst_width; ++i) {
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_val = clip((p->scaler_a.dst[i] + 8) >> 4, 15);
|
||||
dst[2 * i] = (dst[2 * i] & 0xf0) | alpha_val;
|
||||
const uint32_t alpha_val = VP8Clip4Bits(p->scaler_a.dst[i]);
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_val;
|
||||
}
|
||||
dst += buf->stride;
|
||||
alpha_dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
if (io->a != NULL) {
|
||||
int (* const output_func)(WebPDecParams* const, int) =
|
||||
(p->output->colorspace == MODE_RGBA_4444) ? ExportAlphaRGBA4444
|
||||
: ExportAlpha;
|
||||
WebPRescaler* const scaler = &p->scaler_a;
|
||||
int j = 0, pos = 0;
|
||||
int j = 0;
|
||||
int pos = 0;
|
||||
while (j < io->mb_h) {
|
||||
j += WebPRescalerImport(scaler, io->mb_h - j,
|
||||
io->a + j * io->width, io->width);
|
||||
pos += output_func(p, pos);
|
||||
pos += p->emit_alpha_row(p, pos);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int has_alpha = IsAlphaMode(p->output->colorspace);
|
||||
const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
|
||||
const int out_width = io->scaled_width;
|
||||
const int out_height = io->scaled_height;
|
||||
const int uv_in_width = (io->mb_w + 1) >> 1;
|
||||
@ -459,6 +491,12 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
io->mb_w, out_width, io->mb_h, out_height,
|
||||
work + 3 * work_size);
|
||||
p->emit_alpha = EmitRescaledAlphaRGB;
|
||||
if (p->output->colorspace == MODE_RGBA_4444 ||
|
||||
p->output->colorspace == MODE_rgbA_4444) {
|
||||
p->emit_alpha_row = ExportAlphaRGBA4444;
|
||||
} else {
|
||||
p->emit_alpha_row = ExportAlpha;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -469,12 +507,13 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int CustomSetup(VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int is_rgb = (colorspace < MODE_YUV);
|
||||
const int is_alpha = IsAlphaMode(colorspace);
|
||||
const int is_rgb = WebPIsRGBMode(colorspace);
|
||||
const int is_alpha = WebPIsAlphaMode(colorspace);
|
||||
|
||||
p->memory = NULL;
|
||||
p->emit = NULL;
|
||||
p->emit_alpha = NULL;
|
||||
p->emit_alpha_row = NULL;
|
||||
if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
|
||||
return 0;
|
||||
}
|
||||
@ -505,10 +544,12 @@ static int CustomSetup(VP8Io* io) {
|
||||
p->emit = EmitYUV;
|
||||
}
|
||||
if (is_alpha) { // need transparency output
|
||||
if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
|
||||
p->emit_alpha =
|
||||
is_rgb ? (colorspace == MODE_RGBA_4444 ? EmitAlphaRGBA4444
|
||||
: EmitAlphaRGB)
|
||||
: EmitAlphaYUV;
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ struct VP8Decoder {
|
||||
// extensions
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
size_t alpha_data_size_;
|
||||
uint8_t* alpha_plane_; // output
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
|
||||
int layer_colorspace_;
|
||||
const uint8_t* layer_data_; // compressed layer data (if present)
|
||||
|
@ -477,7 +477,7 @@ static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data,
|
||||
WebPCopyDecBuffer(&output, keep_info);
|
||||
}
|
||||
// return decoded samples (don't clear 'output'!)
|
||||
return (mode >= MODE_YUV) ? output.u.YUVA.y : output.u.RGBA.rgba;
|
||||
return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
@ -685,7 +685,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
h = options->crop_height;
|
||||
x = options->crop_left;
|
||||
y = options->crop_top;
|
||||
if (src_colorspace >= MODE_YUV) { // only snap for YUV420 or YUV422
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
|
||||
x &= ~1;
|
||||
y &= ~1; // TODO(later): only for YUV420, not YUV422.
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ extern "C" {
|
||||
|
||||
typedef struct WebPDecParams WebPDecParams;
|
||||
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
|
||||
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
|
||||
|
||||
struct WebPDecParams {
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
@ -34,9 +35,11 @@ struct WebPDecParams {
|
||||
const WebPDecoderOptions* options; // if not NULL, use alt decoding features
|
||||
// rescalers
|
||||
WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
OutputFunc emit; // output RGB or YUV samples
|
||||
OutputFunc emit_alpha; // output alpha channel
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
|
||||
OutputFunc emit; // output RGB or YUV samples
|
||||
OutputFunc emit_alpha; // output alpha channel
|
||||
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
|
||||
};
|
||||
|
||||
// Should be called first, before any use of the WebPDecParams object.
|
||||
|
Reference in New Issue
Block a user