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
This commit is contained in:
Vikas Arora 2011-12-01 08:57:36 +05:30
parent e1947a9299
commit d117a94046
8 changed files with 62 additions and 77 deletions

View File

@ -5,7 +5,8 @@ libwebpdecode_la_SOURCES = vp8i.h webpi.h \
idec.c alpha.c layer.c io.c buffer.c idec.c alpha.c layer.c io.c buffer.c
libwebpdecode_la_LDFLAGS = -version-info 2:0:0 libwebpdecode_la_LDFLAGS = -version-info 2:0:0
libwebpdecode_la_CPPFLAGS = $(USE_EXPERIMENTAL_CODE) 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 libwebpdecodeincludedir = $(includedir)/webp
noinst_LTLIBRARIES = libwebpdecode.la noinst_LTLIBRARIES = libwebpdecode.la

View File

@ -10,11 +10,8 @@
// Author: Skal (pascal.massimino@gmail.com) // Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h" #include "./vp8i.h"
#include "../utils/alpha.h"
#ifdef WEBP_EXPERIMENTAL_FEATURES
#include "zlib.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
@ -24,46 +21,25 @@ extern "C" {
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
int row, int num_rows) { int row, int num_rows) {
uint8_t* output = dec->alpha_plane_;
const int stride = dec->pic_hdr_.width_; 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) { if (row == 0) {
// TODO(skal): for now, we just decompress everything during the first call. // Decode everything during the first call.
// Later, we'll decode progressively, but we need to store the if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
// z_stream state. dec->pic_hdr_.width_, dec->pic_hdr_.height_, stride,
const uint8_t* data = dec->alpha_data_; dec->alpha_plane_)) {
size_t data_size = dec->alpha_data_size_; return NULL; // Error.
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
} }
} }
return output + row * stride;
// Return a pointer to the current decoded row.
return dec->alpha_plane_ + row * stride;
} }
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} // extern "C" } // extern "C"
#endif #endif
#endif // WEBP_EXPERIMENTAL_FEATURES

View File

@ -258,7 +258,9 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
uint32_t bytes_skipped; uint32_t bytes_skipped;
VP8StatusCode status; 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) { if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
} else if (status == VP8_STATUS_OK) { } else if (status == VP8_STATUS_OK) {

View File

@ -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) { static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
const int mb_w = io->mb_w; const int mb_w = io->mb_w;
const int mb_h = io->mb_h; const int mb_h = io->mb_h;
@ -200,8 +199,6 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
return 0; return 0;
} }
#endif /* WEBP_EXPERIMENTAL_FEATURES */
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Simple picture rescaler // Simple picture rescaler
@ -609,12 +606,10 @@ static int CustomSetup(VP8Io* io) {
} else { } else {
p->emit = EmitYUV; p->emit = EmitYUV;
} }
#ifdef WEBP_EXPERIMENTAL_FEATURES
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 ? EmitAlphaRGB : EmitAlphaYUV;
} }
#endif
} }
if (is_rgb) { if (is_rgb) {

View File

@ -80,6 +80,13 @@ int VP8SetError(VP8Decoder* const dec,
int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size, int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size,
int* width, int* height, int* has_alpha) { 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) { if (data_size < 10) {
return 0; // not enough data 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 w = ((data[7] << 8) | data[6]) & 0x3fff;
const int h = ((data[9] << 8) | data[8]) & 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. if (!key_frame) { // Not a keyframe.
return 0; return 0;
} }
@ -269,7 +268,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
buf_size = io->data_size; buf_size = io->data_size;
// 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_);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return VP8SetError(dec, status, "Incorrect/incomplete header."); return VP8SetError(dec, status, "Incorrect/incomplete header.");
} }
@ -342,9 +342,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
"bad partition length"); "bad partition length");
} }
dec->alpha_data_ = NULL;
dec->alpha_data_size_ = 0;
br = &dec->br_; br = &dec->br_;
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
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 || if (frm_hdr->partition_length_ < kTrailerSize ||
ext_buf[kTrailerSize - 1] != kTrailerMarker) { ext_buf[kTrailerSize - 1] != kTrailerMarker) {
Error:
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
"RIFF: Inconsistent extra information."); "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 // Layer
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16); size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);

View File

@ -273,7 +273,7 @@ struct VP8Decoder {
// extensions // extensions
const uint8_t* alpha_data_; // compressed alpha data (if present) 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 uint8_t* alpha_plane_; // output
int layer_colorspace_; int layer_colorspace_;

View File

@ -12,6 +12,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "vp8i.h" #include "vp8i.h"
#include "webpi.h" #include "webpi.h"
#include "../webp/mux.h" // For 'ALPHA_FLAG'.
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { 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, VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size,
uint32_t riff_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; const uint8_t* buf;
uint32_t buf_size; uint32_t buf_size;
assert(data); assert(data);
assert(data_size); assert(data_size);
assert(bytes_skipped); assert(bytes_skipped);
assert(alpha_data);
assert(alpha_size);
buf = *data; buf = *data;
buf_size = *data_size; buf_size = *data_size;
*bytes_skipped = 0; *bytes_skipped = 0;
*alpha_data = NULL;
*alpha_size = 0;
while (1) { while (1) {
uint32_t chunk_size; uint32_t chunk_size;
@ -145,7 +152,10 @@ VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size,
return VP8_STATUS_NOT_ENOUGH_DATA; 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. 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, 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; const uint8_t* buf;
uint32_t buf_size; uint32_t buf_size;
uint32_t riff_size; uint32_t riff_size;
@ -200,12 +212,16 @@ VP8StatusCode WebPParseHeaders(const uint8_t** data, uint32_t* data_size,
assert(data_size); assert(data_size);
assert(vp8_size); assert(vp8_size);
assert(bytes_skipped); assert(bytes_skipped);
assert(alpha_data);
assert(alpha_size);
buf = *data; buf = *data;
buf_size = *data_size; buf_size = *data_size;
*vp8_size = 0; *vp8_size = 0;
*bytes_skipped = 0; *bytes_skipped = 0;
*alpha_data = NULL;
*alpha_size = 0;
if (buf == NULL || buf_size < RIFF_HEADER_SIZE) { if (buf == NULL || buf_size < RIFF_HEADER_SIZE) {
return VP8_STATUS_NOT_ENOUGH_DATA; 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) { if (vp8x_skip_size > 0) {
// Skip over optional chunks. // Skip over optional chunks.
status = WebPParseOptionalChunks(&buf, &buf_size, riff_size, status = WebPParseOptionalChunks(&buf, &buf_size, riff_size,
&optional_data_size); &optional_data_size,
alpha_data, alpha_size);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Found an invalid chunk size / Insufficient data. 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); &features->height, &flags);
if (status != VP8_STATUS_OK) { if (status != VP8_STATUS_OK) {
return status; // Wrong VP8X / insufficient data. return status; // Wrong VP8X / insufficient data.
} }
if (vp8x_skip_size > 0) { if (vp8x_skip_size > 0) {
return VP8_STATUS_OK; // Return features from VP8X header. return VP8_STATUS_OK; // Return features from VP8X header.
} }
features->has_alpha = !!(flags & ALPHA_FLAG);
// Skip over VP8 header. // Skip over VP8 header.
status = WebPParseVP8Header(&data, &data_size, riff_size, &vp8_skip_size, 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. // Validates raw VP8 data.
if (!VP8GetInfo(data, data_size, vp8_chunk_size, 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; return VP8_STATUS_BITSTREAM_ERROR;
} }

View File

@ -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_NOT_ENOUGH_DATA in case of insufficient data, and
// VP8_STATUS_OK otherwise. // VP8_STATUS_OK otherwise.
// If a VP8 chunk is found, bytes_skipped is set to the total number of bytes // 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, VP8StatusCode WebPParseOptionalChunks(const uint8_t** data, uint32_t* data_size,
uint32_t riff_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. // Validates the VP8 Header ("VP8 nnnn") and skips over it.
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (vp8_chunk_size greater than // 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, // Returns VP8_STATUS_OK on success,
// VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and // VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and
// VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data. // VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data.
// Also, data, data_size, vp8_size & bytes_skipped are updated appropriately // Also, data, data_size, vp8_size, bytes_skipped, alpha_data & alpha_size are
// on success, where // updated appropriately on success, where
// vp8_size is the size of VP8 chunk data (extracted from VP8 chunk header) and // 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. // bytes_skipped is set to the total number of bytes that are skipped.
VP8StatusCode WebPParseHeaders(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);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Misc utils // Misc utils