Merge "Add Alpha Decode support from WebPDecode."
This commit is contained in:
commit
16612ddd30
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user