diff --git a/src/dec/frame.c b/src/dec/frame.c index 00c735af..396cc369 100644 --- a/src/dec/frame.c +++ b/src/dec/frame.c @@ -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. } 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); if (io->a == NULL) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, diff --git a/src/dec/io.c b/src/dec/io.c index d05e971b..14b2233e 100644 --- a/src/dec/io.c +++ b/src/dec/io.c @@ -51,7 +51,9 @@ 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 = WebPSamplers[output->colorspace]; + const WebPSampleLinePairFunc sample = + io->a ? WebPSamplersKeepAlpha[output->colorspace] + : WebPSamplers[output->colorspace]; const int mb_w = io->mb_w; const int last = io->mb_h - 1; int j; @@ -185,12 +187,34 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { 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; + 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 + 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; 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) { 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; 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) { - 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; num_lines_out++; @@ -460,7 +504,8 @@ static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { int j = 0, pos = 0; while (j < io->mb_h) { 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; @@ -608,7 +653,9 @@ static int CustomSetup(VP8Io* io) { } if (IsAlphaMode(p->output->colorspace)) { // 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; } } diff --git a/src/dec/vp8.c b/src/dec/vp8.c index ef119e85..3c1457e0 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -249,6 +249,8 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { const uint8_t* buf; uint32_t buf_size; + const uint8_t* alpha_data_tmp; + uint32_t alpha_size_tmp; uint32_t vp8_chunk_size; uint32_t bytes_skipped; VP8FrameHeader* frm_hdr; @@ -270,10 +272,19 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { // Process Pre-VP8 chunks. 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) { 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. if (buf_size < 4) { diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h index aa6fd3dd..725a2c8e 100644 --- a/src/dsp/dsp.h +++ b/src/dsp/dsp.h @@ -155,6 +155,7 @@ typedef void (*WebPSampleLinePairFunc)( uint8_t* top_dst, uint8_t* bottom_dst, int len); extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */]; +extern const WebPSampleLinePairFunc WebPSamplersKeepAlpha[/* MODE_LAST */]; // YUV444->RGB converters typedef void (*WebPYUV444Converter)(const uint8_t* y, diff --git a/src/dsp/upsampling.c b/src/dsp/upsampling.c index c88a17ad..98168642 100644 --- a/src/dsp/upsampling.c +++ b/src/dsp/upsampling.c @@ -101,7 +101,7 @@ UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4) UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 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(UpsampleBgrKeepAlphaLinePair, VP8YuvToBgr, 4) UPSAMPLE_FUNC(UpsampleArgbKeepAlphaLinePair, VP8YuvToArgbKeepA, 4) @@ -146,6 +146,11 @@ SAMPLE_FUNC(SampleBgraLinePair, VP8YuvToBgra, 4) SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4) SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 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 @@ -159,6 +164,16 @@ const WebPSampleLinePairFunc WebPSamplers[MODE_LAST] = { 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