Some bug-fixes for images with alpha.

- Fix the off-by-one diff when cropping with simple-filter.
- Fix a bug in incremental decoding in case of alpha.
- In VP8FinishRow(), do not decode alpha when y_start > y_end.
- Correct output of alpha channel for MODE_ARGB.
- Correct output of alpha channel for MODE_RGBA_4444.

Change-Id: I785763a2a704b973cc742ad93ffbb53699d1fc0a
This commit is contained in:
Urvang Joshi 2011-12-07 15:01:35 +05:30
parent 273a12a013
commit 8666a93aae
5 changed files with 84 additions and 10 deletions

View File

@ -365,7 +365,7 @@ int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
y_end = io->crop_bottom; // make sure we don't overflow on last row. y_end = io->crop_bottom; // make sure we don't overflow on last row.
} }
io->a = NULL; io->a = NULL;
if (dec->alpha_data_) { if (dec->alpha_data_ && y_start < y_end) {
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start); io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
if (io->a == NULL) { if (io->a == NULL) {
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,

View File

@ -51,7 +51,9 @@ static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
const uint8_t* y_src = io->y; const uint8_t* y_src = io->y;
const uint8_t* u_src = io->u; const uint8_t* u_src = io->u;
const uint8_t* v_src = io->v; const uint8_t* v_src = io->v;
const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace]; const WebPSampleLinePairFunc sample =
io->a ? WebPSamplersKeepAlpha[output->colorspace]
: WebPSamplers[output->colorspace];
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int last = io->mb_h - 1; const int last = io->mb_h - 1;
int j; int j;
@ -185,12 +187,34 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
int i, j; int i, j;
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + io->mb_y * buf->stride; uint8_t* dst = buf->rgba + io->mb_y * buf->stride +
(p->output->colorspace == MODE_ARGB ? 0 : 3);
const uint8_t* alpha = io->a; const uint8_t* alpha = io->a;
if (alpha) { if (alpha) {
for (j = 0; j < mb_h; ++j) { for (j = 0; j < mb_h; ++j) {
for (i = 0; i < mb_w; ++i) { for (i = 0; i < mb_w; ++i) {
dst[4 * i + 3] = alpha[i]; dst[4 * i] = alpha[i];
}
alpha += io->width;
dst += buf->stride;
}
}
return 0;
}
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) {
for (j = 0; j < mb_h; ++j) {
for (i = 0; i < mb_w; ++i) {
// Fill in the alpha value (converted to 4 bits).
const uint8_t alpha_val = (alpha[i] + 8) >> 4;
dst[2 * i] = (dst[2 * i] & 0xf0) | alpha_val;
} }
alpha += io->width; alpha += io->width;
dst += buf->stride; dst += buf->stride;
@ -440,14 +464,34 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
static int ExportAlpha(WebPDecParams* const p, int y_pos) { static int ExportAlpha(WebPDecParams* const p, int y_pos) {
const WebPRGBABuffer* const buf = &p->output->u.RGBA; const WebPRGBABuffer* const buf = &p->output->u.RGBA;
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride; uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride +
(p->output->colorspace == MODE_ARGB ? 0 : 3);
int num_lines_out = 0; int num_lines_out = 0;
while (p->scaler_a.y_accum <= 0) { while (p->scaler_a.y_accum <= 0) {
int i; int i;
assert(p->last_y + y_pos + num_lines_out < p->output->height); assert(p->last_y + y_pos + num_lines_out < p->output->height);
ExportRow(&p->scaler_a); ExportRow(&p->scaler_a);
for (i = 0; i < p->scaler_a.dst_width; ++i) { for (i = 0; i < p->scaler_a.dst_width; ++i) {
dst[4 * i + 3] = p->scaler_a.dst[i]; dst[4 * i] = p->scaler_a.dst[i];
}
dst += buf->stride;
num_lines_out++;
}
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;
int num_lines_out = 0;
while (p->scaler_a.y_accum <= 0) {
int i;
assert(p->last_y + y_pos + num_lines_out < p->output->height);
ExportRow(&p->scaler_a);
for (i = 0; i < p->scaler_a.dst_width; ++i) {
// Fill in the alpha value (converted to 4 bits).
const uint8_t alpha_val = (p->scaler_a.dst[i] + 8) >> 4;
dst[2 * i] = (dst[2 * i] & 0xf0) | alpha_val;
} }
dst += buf->stride; dst += buf->stride;
num_lines_out++; num_lines_out++;
@ -460,7 +504,8 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
int j = 0, pos = 0; int j = 0, pos = 0;
while (j < io->mb_h) { while (j < io->mb_h) {
j += Import(io->a + j * io->width, io->width, io->mb_h - j, &p->scaler_a); j += Import(io->a + j * io->width, io->width, io->mb_h - j, &p->scaler_a);
pos += ExportAlpha(p, pos); pos += (p->output->colorspace == MODE_RGBA_4444) ?
ExportAlphaRGBA4444(p, pos) : ExportAlpha(p, pos);
} }
} }
return 0; return 0;
@ -608,7 +653,9 @@ static int CustomSetup(VP8Io* io) {
} }
if (IsAlphaMode(p->output->colorspace)) { if (IsAlphaMode(p->output->colorspace)) {
// We need transparency output // We need transparency output
p->emit_alpha = is_rgb ? EmitAlphaRGB : EmitAlphaYUV; p->emit_alpha =
is_rgb ? (p->output->colorspace == MODE_RGBA_4444 ?
EmitAlphaRGBA4444 : EmitAlphaRGB) : EmitAlphaYUV;
} }
} }

View File

@ -249,6 +249,8 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
const uint8_t* buf; const uint8_t* buf;
uint32_t buf_size; uint32_t buf_size;
const uint8_t* alpha_data_tmp;
uint32_t alpha_size_tmp;
uint32_t vp8_chunk_size; uint32_t vp8_chunk_size;
uint32_t bytes_skipped; uint32_t bytes_skipped;
VP8FrameHeader* frm_hdr; VP8FrameHeader* frm_hdr;
@ -270,10 +272,19 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
// Process Pre-VP8 chunks. // Process Pre-VP8 chunks.
status = WebPParseHeaders(&buf, &buf_size, &vp8_chunk_size, &bytes_skipped, status = WebPParseHeaders(&buf, &buf_size, &vp8_chunk_size, &bytes_skipped,
&dec->alpha_data_, &dec->alpha_data_size_); &alpha_data_tmp, &alpha_size_tmp);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return VP8SetError(dec, status, "Incorrect/incomplete header."); return VP8SetError(dec, status, "Incorrect/incomplete header.");
} }
if (dec->alpha_data_ == NULL) {
assert(dec->alpha_data_size_ == 0);
// We have NOT set alpha data yet. Set it now.
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
// WebPParseHeaders() is called more than once, as in incremental decoding
// case.)
dec->alpha_data_ = alpha_data_tmp;
dec->alpha_data_size_ = alpha_size_tmp;
}
// Process the VP8 frame header. // Process the VP8 frame header.
if (buf_size < 4) { if (buf_size < 4) {

View File

@ -155,6 +155,7 @@ typedef void (*WebPSampleLinePairFunc)(
uint8_t* top_dst, uint8_t* bottom_dst, int len); uint8_t* top_dst, uint8_t* bottom_dst, int len);
extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */]; extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */];
extern const WebPSampleLinePairFunc WebPSamplersKeepAlpha[/* MODE_LAST */];
// YUV444->RGB converters // YUV444->RGB converters
typedef void (*WebPYUV444Converter)(const uint8_t* y, typedef void (*WebPYUV444Converter)(const uint8_t* y,

View File

@ -101,7 +101,7 @@ UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4)
UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4)
UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2) UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2)
UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2) UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2)
// These two don't erase the alpha value // These variants don't erase the alpha value
UPSAMPLE_FUNC(UpsampleRgbKeepAlphaLinePair, VP8YuvToRgb, 4) UPSAMPLE_FUNC(UpsampleRgbKeepAlphaLinePair, VP8YuvToRgb, 4)
UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4) UPSAMPLE_FUNC(UpsampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4)
UPSAMPLE_FUNC(UpsampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4) UPSAMPLE_FUNC(UpsampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4)
@ -146,6 +146,11 @@ SAMPLE_FUNC(SampleBgraLinePair, VP8YuvToBgra, 4)
SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4) SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4)
SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 2) SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 2)
SAMPLE_FUNC(SampleRgb565LinePair, VP8YuvToRgb565, 2) SAMPLE_FUNC(SampleRgb565LinePair, VP8YuvToRgb565, 2)
// These variants don't erase the alpha value
SAMPLE_FUNC(SampleRgbKeepAlphaLinePair, VP8YuvToRgb, 4)
SAMPLE_FUNC(SampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4)
SAMPLE_FUNC(SampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4)
SAMPLE_FUNC(SampleRgba4444KeepAlphaLinePair, VP8YuvToRgba4444KeepA, 2)
#undef SAMPLE_FUNC #undef SAMPLE_FUNC
@ -159,6 +164,16 @@ const WebPSampleLinePairFunc WebPSamplers[MODE_LAST] = {
SampleRgb565LinePair // MODE_RGB_565 SampleRgb565LinePair // MODE_RGB_565
}; };
const WebPSampleLinePairFunc WebPSamplersKeepAlpha[MODE_LAST] = {
SampleRgbLinePair, // MODE_RGB
SampleRgbKeepAlphaLinePair, // MODE_RGBA
SampleBgrLinePair, // MODE_BGR
SampleBgrKeepAlphaLinePair, // MODE_BGRA
SampleArgbKeepAlphaLinePair, // MODE_ARGB
SampleRgba4444KeepAlphaLinePair, // MODE_RGBA_4444
SampleRgb565LinePair // MODE_RGB_565
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// YUV444 converter // YUV444 converter