From d117a940465d16064099994fcbcfcbd949f6a190 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Thu, 1 Dec 2011 08:57:36 +0530 Subject: [PATCH] Add Alpha Decode support from WebPDecode. Extend WebP decode functionality to extract Alpha data (support ALPH chunk) and decode the Alpha plane using Alpha (utils/alpha) core-lib. Change-Id: I6f0ee1b189c13caba3c1dd9b681383bfd00aa212 --- src/dec/Makefile.am | 3 ++- src/dec/alpha.c | 52 ++++++++++++--------------------------------- src/dec/idec.c | 4 +++- src/dec/io.c | 5 ----- src/dec/vp8.c | 29 ++++++++----------------- src/dec/vp8i.h | 2 +- src/dec/webp.c | 29 +++++++++++++++++++------ src/dec/webpi.h | 15 ++++++++----- 8 files changed, 62 insertions(+), 77 deletions(-) diff --git a/src/dec/Makefile.am b/src/dec/Makefile.am index 3f66fcff..bd21b391 100644 --- a/src/dec/Makefile.am +++ b/src/dec/Makefile.am @@ -5,7 +5,8 @@ libwebpdecode_la_SOURCES = vp8i.h webpi.h \ idec.c alpha.c layer.c io.c buffer.c libwebpdecode_la_LDFLAGS = -version-info 2:0:0 libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE) -libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h +libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h \ + ../webp/types.h ../webp/mux.h libwebpdecodeincludedir = $(includedir)/webp noinst_LTLIBRARIES = libwebpdecode.la diff --git a/src/dec/alpha.c b/src/dec/alpha.c index 3052ced7..773eb0b7 100644 --- a/src/dec/alpha.c +++ b/src/dec/alpha.c @@ -10,11 +10,8 @@ // Author: Skal (pascal.massimino@gmail.com) #include -#include "vp8i.h" - -#ifdef WEBP_EXPERIMENTAL_FEATURES - -#include "zlib.h" +#include "./vp8i.h" +#include "../utils/alpha.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -24,46 +21,25 @@ extern "C" { const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, int row, int num_rows) { - uint8_t* output = dec->alpha_plane_; const int stride = dec->pic_hdr_.width_; - if (row < 0 || row + num_rows > dec->pic_hdr_.height_) { - return NULL; // sanity check + + if (row < 0 || num_rows < 0 || row + num_rows > dec->pic_hdr_.height_) { + return NULL; // sanity check. } + if (row == 0) { - // TODO(skal): for now, we just decompress everything during the first call. - // Later, we'll decode progressively, but we need to store the - // z_stream state. - const uint8_t* data = dec->alpha_data_; - size_t data_size = dec->alpha_data_size_; - const size_t output_size = stride * dec->pic_hdr_.height_; - int ret = Z_OK; - z_stream strm; - - memset(&strm, 0, sizeof(strm)); - if (inflateInit(&strm) != Z_OK) { - return 0; - } - strm.avail_in = data_size; - strm.next_in = (unsigned char*)data; - do { - strm.avail_out = output_size; - strm.next_out = output; - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { - break; - } - } while (strm.avail_out == 0); - - inflateEnd(&strm); - if (ret != Z_STREAM_END) { - return NULL; // error + // Decode everything during the first call. + if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_, + dec->pic_hdr_.width_, dec->pic_hdr_.height_, stride, + dec->alpha_plane_)) { + return NULL; // Error. } } - return output + row * stride; + + // Return a pointer to the current decoded row. + return dec->alpha_plane_ + row * stride; } #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" #endif - -#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/dec/idec.c b/src/dec/idec.c index f8e1bf7c..a1f1a583 100644 --- a/src/dec/idec.c +++ b/src/dec/idec.c @@ -258,7 +258,9 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) { uint32_t bytes_skipped; VP8StatusCode status; - status = WebPParseHeaders(&data, &curr_size, &vp8_size, &bytes_skipped); + status = WebPParseHeaders(&data, &curr_size, &vp8_size, &bytes_skipped, + &idec->dec_->alpha_data_, + &idec->dec_->alpha_data_size_); if (status == VP8_STATUS_NOT_ENOUGH_DATA) { return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. } else if (status == VP8_STATUS_OK) { diff --git a/src/dec/io.c b/src/dec/io.c index 03ef8721..d05e971b 100644 --- a/src/dec/io.c +++ b/src/dec/io.c @@ -163,7 +163,6 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { //------------------------------------------------------------------------------ -#ifdef WEBP_EXPERIMENTAL_FEATURES static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) { const int mb_w = io->mb_w; const int mb_h = io->mb_h; @@ -200,8 +199,6 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) { return 0; } -#endif /* WEBP_EXPERIMENTAL_FEATURES */ - //------------------------------------------------------------------------------ // Simple picture rescaler @@ -609,12 +606,10 @@ static int CustomSetup(VP8Io* io) { } else { p->emit = EmitYUV; } -#ifdef WEBP_EXPERIMENTAL_FEATURES if (IsAlphaMode(p->output->colorspace)) { // We need transparency output p->emit_alpha = is_rgb ? EmitAlphaRGB : EmitAlphaYUV; } -#endif } if (is_rgb) { diff --git a/src/dec/vp8.c b/src/dec/vp8.c index 9149284b..97837272 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -80,6 +80,13 @@ int VP8SetError(VP8Decoder* const dec, int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size, int* width, int* height, int* has_alpha) { + // Alpha-data is stored outside VP8 data and is inferred from VP8X chunk. + // So, this function always returns *has_alpha = 0. This value should NOT + // be used. + if (has_alpha != NULL) { + *has_alpha = 0; + } + if (data_size < 10) { return 0; // not enough data } @@ -92,14 +99,6 @@ int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size, const int w = ((data[7] << 8) | data[6]) & 0x3fff; const int h = ((data[9] << 8) | data[8]) & 0x3fff; - if (has_alpha) { -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (data_size < 11) return 0; - *has_alpha = !!(data[10] & 0x80); // the colorspace_ bit -#else - *has_alpha = 0; -#endif - } if (!key_frame) { // Not a keyframe. return 0; } @@ -269,7 +268,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { buf_size = io->data_size; // 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_); if (status != VP8_STATUS_OK) { return VP8SetError(dec, status, "Incorrect/incomplete header."); } @@ -342,9 +342,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { "bad partition length"); } - dec->alpha_data_ = NULL; - dec->alpha_data_size_ = 0; - br = &dec->br_; VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); buf += frm_hdr->partition_length_; @@ -418,17 +415,9 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { if (frm_hdr->partition_length_ < kTrailerSize || ext_buf[kTrailerSize - 1] != kTrailerMarker) { - Error: return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "RIFF: Inconsistent extra information."); } - // Alpha - size = (ext_buf[4] << 0) | (ext_buf[5] << 8) | (ext_buf[6] << 16); - if (frm_hdr->partition_length_ < size + kTrailerSize) { - goto Error; - } - dec->alpha_data_ = (size > 0) ? ext_buf - size : NULL; - dec->alpha_data_size_ = size; // Layer size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16); diff --git a/src/dec/vp8i.h b/src/dec/vp8i.h index 2cbdef22..e2edd2c7 100644 --- a/src/dec/vp8i.h +++ b/src/dec/vp8i.h @@ -273,7 +273,7 @@ struct VP8Decoder { // extensions const uint8_t* alpha_data_; // compressed alpha data (if present) - size_t alpha_data_size_; + uint32_t alpha_data_size_; uint8_t* alpha_plane_; // output int layer_colorspace_; diff --git a/src/dec/webp.c b/src/dec/webp.c index f9a9ba5d..6944461e 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -12,6 +12,7 @@ #include #include "vp8i.h" #include "webpi.h" +#include "../webp/mux.h" // For 'ALPHA_FLAG'. #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -105,17 +106,23 @@ VP8StatusCode WebPParseVP8X(const uint8_t** data, uint32_t* data_size, VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size, uint32_t riff_size, - uint32_t* bytes_skipped) { + uint32_t* bytes_skipped, + const uint8_t** alpha_data, + uint32_t* alpha_size) { const uint8_t* buf; uint32_t buf_size; assert(data); assert(data_size); assert(bytes_skipped); + assert(alpha_data); + assert(alpha_size); buf = *data; buf_size = *data_size; *bytes_skipped = 0; + *alpha_data = NULL; + *alpha_size = 0; while (1) { uint32_t chunk_size; @@ -145,7 +152,10 @@ VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size, return VP8_STATUS_NOT_ENOUGH_DATA; } - if (!memcmp(buf, "VP8 ", TAG_SIZE)) { // A valid VP8 header. + if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header. + *alpha_data = buf + CHUNK_HEADER_SIZE; + *alpha_size = chunk_size; + } else if (!memcmp(buf, "VP8 ", TAG_SIZE)) { // A valid VP8 header. return VP8_STATUS_OK; // Found. } @@ -186,7 +196,9 @@ VP8StatusCode WebPParseVP8Header(const uint8_t** data, uint32_t* data_size, } VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size, - uint32_t* vp8_size, uint32_t* bytes_skipped) { + uint32_t* vp8_size, uint32_t* bytes_skipped, + const uint8_t** alpha_data, + uint32_t* alpha_size) { const uint8_t* buf; uint32_t buf_size; uint32_t riff_size; @@ -200,12 +212,16 @@ VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size, assert(data_size); assert(vp8_size); assert(bytes_skipped); + assert(alpha_data); + assert(alpha_size); buf = *data; buf_size = *data_size; *vp8_size = 0; *bytes_skipped = 0; + *alpha_data = NULL; + *alpha_size = 0; if (buf == NULL || buf_size < RIFF_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; @@ -224,7 +240,8 @@ VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size, if (vp8x_skip_size > 0) { // Skip over optional chunks. status = WebPParseOptionalChunks(&buf, &buf_size, riff_size, - &optional_data_size); + &optional_data_size, + alpha_data, alpha_size); if (status != VP8_STATUS_OK) { return status; // Found an invalid chunk size / Insufficient data. } @@ -487,11 +504,11 @@ static VP8StatusCode GetFeatures(const uint8_t* data, uint32_t data_size, &features->height, &flags); if (status != VP8_STATUS_OK) { return status; // Wrong VP8X / insufficient data. - } if (vp8x_skip_size > 0) { return VP8_STATUS_OK; // Return features from VP8X header. } + features->has_alpha = !!(flags & ALPHA_FLAG); // Skip over VP8 header. status = WebPParseVP8Header(&data, &data_size, riff_size, &vp8_skip_size, @@ -505,7 +522,7 @@ static VP8StatusCode GetFeatures(const uint8_t* data, uint32_t data_size, // Validates raw VP8 data. if (!VP8GetInfo(data, data_size, vp8_chunk_size, - &features->width, &features->height, &features->has_alpha)) { + &features->width, &features->height, NULL)) { return VP8_STATUS_BITSTREAM_ERROR; } diff --git a/src/dec/webpi.h b/src/dec/webpi.h index 6c14460b..0ca3c150 100644 --- a/src/dec/webpi.h +++ b/src/dec/webpi.h @@ -95,10 +95,13 @@ VP8StatusCode WebPParseVP8X(const uint8_t** data, uint32_t* data_size, // VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and // VP8_STATUS_OK otherwise. // If a VP8 chunk is found, bytes_skipped is set to the total number of bytes -// that are skipped. +// that are skipped. Also, if an alpha chunk is found, alpha_data and alpha_size +// are set appropriately. VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size, uint32_t riff_size, - uint32_t* bytes_skipped); + uint32_t* bytes_skipped, + const uint8_t** alpha_data, + uint32_t* alpha_size); // Validates the VP8 Header ("VP8 nnnn") and skips over it. // Returns VP8_STATUS_BITSTREAM_ERROR for invalid (vp8_chunk_size greater than @@ -117,12 +120,14 @@ VP8StatusCode WebPParseVP8Header(const uint8_t** data, uint32_t* data_size, // Returns VP8_STATUS_OK on success, // VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and // VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data. -// Also, data, data_size, vp8_size & bytes_skipped are updated appropriately -// on success, where +// Also, data, data_size, vp8_size, bytes_skipped, alpha_data & alpha_size are +// updated appropriately on success, where // vp8_size is the size of VP8 chunk data (extracted from VP8 chunk header) and // bytes_skipped is set to the total number of bytes that are skipped. VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size, - uint32_t* vp8_size, uint32_t* bytes_skipped); + uint32_t* vp8_size, uint32_t* bytes_skipped, + const uint8_t** alpha_data, + uint32_t* alpha_size); //------------------------------------------------------------------------------ // Misc utils