Merge "Add Alpha Decode support from WebPDecode."

This commit is contained in:
pascal massimino 2011-11-30 22:02:03 -08:00 committed by Gerrit Code Review
commit 16612ddd30
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
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

View File

@ -10,11 +10,8 @@
// Author: Skal (pascal.massimino@gmail.com)
#include <stdlib.h>
#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

View File

@ -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) {

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) {
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) {

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* 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);

View File

@ -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_;

View File

@ -12,6 +12,7 @@
#include <stdlib.h>
#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;
}

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_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