2012-01-06 23:49:06 +01:00
|
|
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
2010-09-30 15:34:38 +02:00
|
|
|
//
|
|
|
|
// This code is licensed under the same terms as WebM:
|
|
|
|
// Software License Agreement: http://www.webmproject.org/license/software/
|
|
|
|
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// main entry for the decoder
|
|
|
|
//
|
|
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2011-12-01 11:00:03 +01:00
|
|
|
|
|
|
|
#include "./vp8i.h"
|
|
|
|
#include "./webpi.h"
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-03-10 06:25:53 +01:00
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-03-25 00:17:10 +01:00
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
int WebPGetDecoderVersion(void) {
|
2011-03-25 00:17:10 +01:00
|
|
|
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 15:34:38 +02:00
|
|
|
// VP8Decoder
|
|
|
|
|
|
|
|
static void SetOk(VP8Decoder* const dec) {
|
2011-02-16 23:33:16 +01:00
|
|
|
dec->status_ = VP8_STATUS_OK;
|
2010-09-30 15:34:38 +02:00
|
|
|
dec->error_msg_ = "OK";
|
|
|
|
}
|
|
|
|
|
2011-02-27 19:51:01 +01:00
|
|
|
int VP8InitIoInternal(VP8Io* const io, int version) {
|
|
|
|
if (version != WEBP_DECODER_ABI_VERSION)
|
|
|
|
return 0; // mismatch error
|
2010-09-30 15:34:38 +02:00
|
|
|
if (io) {
|
|
|
|
memset(io, 0, sizeof(*io));
|
|
|
|
}
|
2011-02-27 19:51:01 +01:00
|
|
|
return 1;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
2011-03-25 23:04:11 +01:00
|
|
|
VP8Decoder* VP8New(void) {
|
2012-01-28 02:39:47 +01:00
|
|
|
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder));
|
2010-09-30 15:34:38 +02:00
|
|
|
if (dec) {
|
|
|
|
SetOk(dec);
|
2011-07-22 22:09:10 +02:00
|
|
|
WebPWorkerInit(&dec->worker_);
|
2010-09-30 15:34:38 +02:00
|
|
|
dec->ready_ = 0;
|
|
|
|
}
|
|
|
|
return dec;
|
|
|
|
}
|
|
|
|
|
2011-02-16 23:33:16 +01:00
|
|
|
VP8StatusCode VP8Status(VP8Decoder* const dec) {
|
|
|
|
if (!dec) return VP8_STATUS_INVALID_PARAM;
|
2010-09-30 15:34:38 +02:00
|
|
|
return dec->status_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* VP8StatusMessage(VP8Decoder* const dec) {
|
|
|
|
if (!dec) return "no object";
|
|
|
|
if (!dec->error_msg_) return "OK";
|
|
|
|
return dec->error_msg_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VP8Delete(VP8Decoder* const dec) {
|
|
|
|
if (dec) {
|
|
|
|
VP8Clear(dec);
|
|
|
|
free(dec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-16 23:33:16 +01:00
|
|
|
int VP8SetError(VP8Decoder* const dec,
|
|
|
|
VP8StatusCode error, const char * const msg) {
|
2010-09-30 15:34:38 +02:00
|
|
|
dec->status_ = error;
|
|
|
|
dec->error_msg_ = msg;
|
|
|
|
dec->ready_ = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-05-20 04:07:01 +02:00
|
|
|
|
2011-08-18 20:07:13 +02:00
|
|
|
int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size,
|
2011-12-08 15:39:53 +01:00
|
|
|
int* width, int* height) {
|
2011-06-20 09:45:15 +02:00
|
|
|
if (data_size < 10) {
|
|
|
|
return 0; // not enough data
|
|
|
|
}
|
2011-05-20 04:07:01 +02:00
|
|
|
// check signature
|
|
|
|
if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) {
|
|
|
|
return 0; // Wrong signature.
|
|
|
|
} else {
|
|
|
|
const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
|
|
|
const int key_frame = !(bits & 1);
|
|
|
|
const int w = ((data[7] << 8) | data[6]) & 0x3fff;
|
|
|
|
const int h = ((data[9] << 8) | data[8]) & 0x3fff;
|
|
|
|
|
|
|
|
if (!key_frame) { // Not a keyframe.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (((bits >> 1) & 7) > 3) {
|
|
|
|
return 0; // unknown profile
|
|
|
|
}
|
|
|
|
if (!((bits >> 4) & 1)) {
|
|
|
|
return 0; // first frame is invisible!
|
|
|
|
}
|
|
|
|
if (((bits >> 5)) >= chunk_size) { // partition_length
|
|
|
|
return 0; // inconsistent size information.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width) {
|
|
|
|
*width = w;
|
|
|
|
}
|
|
|
|
if (height) {
|
|
|
|
*height = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 15:34:38 +02:00
|
|
|
// Header parsing
|
|
|
|
|
|
|
|
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
|
|
|
|
assert(hdr);
|
|
|
|
hdr->use_segment_ = 0;
|
|
|
|
hdr->update_map_ = 0;
|
|
|
|
hdr->absolute_delta_ = 1;
|
|
|
|
memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
|
|
|
|
memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.3
|
|
|
|
static int ParseSegmentHeader(VP8BitReader* br,
|
|
|
|
VP8SegmentHeader* hdr, VP8Proba* proba) {
|
|
|
|
assert(br);
|
|
|
|
assert(hdr);
|
|
|
|
hdr->use_segment_ = VP8Get(br);
|
|
|
|
if (hdr->use_segment_) {
|
|
|
|
hdr->update_map_ = VP8Get(br);
|
2010-10-06 01:58:47 +02:00
|
|
|
if (VP8Get(br)) { // update data
|
|
|
|
int s;
|
2010-09-30 15:34:38 +02:00
|
|
|
hdr->absolute_delta_ = VP8Get(br);
|
2010-10-06 01:58:47 +02:00
|
|
|
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
2010-09-30 15:34:38 +02:00
|
|
|
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
2010-09-30 15:34:38 +02:00
|
|
|
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hdr->update_map_) {
|
2010-10-06 01:58:47 +02:00
|
|
|
int s;
|
|
|
|
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
2010-09-30 15:34:38 +02:00
|
|
|
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hdr->update_map_ = 0;
|
|
|
|
}
|
2011-02-27 19:51:01 +01:00
|
|
|
return !br->eof_;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.5
|
2011-03-11 00:05:59 +01:00
|
|
|
// This function returns VP8_STATUS_SUSPENDED if we don't have all the
|
|
|
|
// necessary data in 'buf'.
|
|
|
|
// This case is not necessarily an error (for incremental decoding).
|
|
|
|
// Still, no bitreader is ever initialized to make it possible to read
|
|
|
|
// unavailable memory.
|
|
|
|
// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
|
|
|
|
// is returned, and this is an unrecoverable error.
|
|
|
|
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
|
|
|
|
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
|
|
|
const uint8_t* buf, uint32_t size) {
|
2010-09-30 15:34:38 +02:00
|
|
|
VP8BitReader* const br = &dec->br_;
|
|
|
|
const uint8_t* sz = buf;
|
2011-03-11 00:05:59 +01:00
|
|
|
const uint8_t* buf_end = buf + size;
|
|
|
|
const uint8_t* part_start;
|
2010-10-06 01:58:47 +02:00
|
|
|
int last_part;
|
|
|
|
int p;
|
|
|
|
|
|
|
|
dec->num_parts_ = 1 << VP8GetValue(br, 2);
|
|
|
|
last_part = dec->num_parts_ - 1;
|
2011-03-11 00:05:59 +01:00
|
|
|
part_start = buf + last_part * 3;
|
|
|
|
if (buf_end < part_start) {
|
|
|
|
// we can't even read the sizes with sz[]! That's a failure.
|
|
|
|
return VP8_STATUS_NOT_ENOUGH_DATA;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
for (p = 0; p < last_part; ++p) {
|
2010-09-30 15:34:38 +02:00
|
|
|
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
2011-03-11 00:05:59 +01:00
|
|
|
const uint8_t* part_end = part_start + psize;
|
|
|
|
if (part_end > buf_end) part_end = buf_end;
|
|
|
|
VP8InitBitReader(dec->parts_ + p, part_start, part_end);
|
|
|
|
part_start = part_end;
|
2010-09-30 15:34:38 +02:00
|
|
|
sz += 3;
|
|
|
|
}
|
2011-03-11 00:05:59 +01:00
|
|
|
VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
|
|
|
|
return (part_start < buf_end) ? VP8_STATUS_OK :
|
|
|
|
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.4
|
|
|
|
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
|
|
|
VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
|
|
|
hdr->simple_ = VP8Get(br);
|
|
|
|
hdr->level_ = VP8GetValue(br, 6);
|
|
|
|
hdr->sharpness_ = VP8GetValue(br, 3);
|
|
|
|
hdr->use_lf_delta_ = VP8Get(br);
|
|
|
|
if (hdr->use_lf_delta_) {
|
|
|
|
if (VP8Get(br)) { // update lf-delta?
|
2010-10-06 01:58:47 +02:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
2010-09-30 15:34:38 +02:00
|
|
|
if (VP8Get(br)) {
|
|
|
|
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
|
|
|
}
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
2010-09-30 15:34:38 +02:00
|
|
|
if (VP8Get(br)) {
|
|
|
|
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
|
|
|
if (dec->filter_type_ > 0) { // precompute filter levels per segment
|
|
|
|
if (dec->segment_hdr_.use_segment_) {
|
2010-10-06 01:58:47 +02:00
|
|
|
int s;
|
|
|
|
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
2010-09-30 15:34:38 +02:00
|
|
|
int strength = dec->segment_hdr_.filter_strength_[s];
|
|
|
|
if (!dec->segment_hdr_.absolute_delta_) {
|
|
|
|
strength += hdr->level_;
|
|
|
|
}
|
|
|
|
dec->filter_levels_[s] = strength;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dec->filter_levels_[0] = hdr->level_;
|
|
|
|
}
|
|
|
|
}
|
2011-02-27 19:51:01 +01:00
|
|
|
return !br->eof_;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Topmost call
|
|
|
|
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
2011-08-18 20:07:13 +02:00
|
|
|
const uint8_t* buf;
|
2010-10-06 01:58:47 +02:00
|
|
|
uint32_t buf_size;
|
2011-12-07 10:31:35 +01:00
|
|
|
const uint8_t* alpha_data_tmp;
|
|
|
|
uint32_t alpha_size_tmp;
|
2011-09-13 11:35:10 +02:00
|
|
|
uint32_t vp8_chunk_size;
|
|
|
|
uint32_t bytes_skipped;
|
2010-10-06 01:58:47 +02:00
|
|
|
VP8FrameHeader* frm_hdr;
|
|
|
|
VP8PictureHeader* pic_hdr;
|
|
|
|
VP8BitReader* br;
|
2011-03-24 01:22:07 +01:00
|
|
|
VP8StatusCode status;
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (dec == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
SetOk(dec);
|
2010-12-20 14:45:49 +01:00
|
|
|
if (io == NULL) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
|
|
|
"null VP8Io passed to VP8GetHeaders()");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
|
2011-09-16 21:07:55 +02:00
|
|
|
buf = io->data;
|
2010-10-06 01:58:47 +02:00
|
|
|
buf_size = io->data_size;
|
2011-08-18 20:07:13 +02:00
|
|
|
|
2011-09-13 11:35:10 +02:00
|
|
|
// Process Pre-VP8 chunks.
|
2011-12-01 04:27:36 +01:00
|
|
|
status = WebPParseHeaders(&buf, &buf_size, &vp8_chunk_size, &bytes_skipped,
|
2011-12-07 10:31:35 +01:00
|
|
|
&alpha_data_tmp, &alpha_size_tmp);
|
2011-09-13 11:35:10 +02:00
|
|
|
if (status != VP8_STATUS_OK) {
|
|
|
|
return VP8SetError(dec, status, "Incorrect/incomplete header.");
|
2011-08-18 20:07:13 +02:00
|
|
|
}
|
2011-12-07 10:31:35 +01:00
|
|
|
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;
|
|
|
|
}
|
2011-08-18 20:07:13 +02:00
|
|
|
|
2011-09-13 11:35:10 +02:00
|
|
|
// Process the VP8 frame header.
|
2011-08-18 20:07:13 +02:00
|
|
|
if (buf_size < 4) {
|
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
2011-09-13 11:35:10 +02:00
|
|
|
"Truncated header.");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.1
|
2010-10-06 01:58:47 +02:00
|
|
|
{
|
|
|
|
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
2010-10-19 23:16:49 +02:00
|
|
|
frm_hdr = &dec->frm_hdr_;
|
2010-10-06 01:58:47 +02:00
|
|
|
frm_hdr->key_frame_ = !(bits & 1);
|
|
|
|
frm_hdr->profile_ = (bits >> 1) & 7;
|
|
|
|
frm_hdr->show_ = (bits >> 4) & 1;
|
|
|
|
frm_hdr->partition_length_ = (bits >> 5);
|
2011-02-27 19:51:01 +01:00
|
|
|
if (frm_hdr->profile_ > 3)
|
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"Incorrect keyframe parameters.");
|
|
|
|
if (!frm_hdr->show_)
|
|
|
|
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
|
|
|
"Frame not displayable.");
|
2010-10-06 01:58:47 +02:00
|
|
|
buf += 3;
|
|
|
|
buf_size -= 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
pic_hdr = &dec->pic_hdr_;
|
2010-09-30 15:34:38 +02:00
|
|
|
if (frm_hdr->key_frame_) {
|
|
|
|
// Paragraph 9.2
|
|
|
|
if (buf_size < 7) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"cannot parse picture header");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
if (buf[0] != 0x9d || buf[1] != 0x01 || buf[2] != 0x2a) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"Bad code word");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
|
|
|
|
pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
|
|
|
|
pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
|
|
|
|
pic_hdr->yscale_ = buf[6] >> 6;
|
|
|
|
buf += 7;
|
|
|
|
buf_size -= 7;
|
|
|
|
|
|
|
|
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
|
|
|
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
2011-06-20 09:45:15 +02:00
|
|
|
// Setup default output area (can be later modified during io->setup())
|
2010-09-30 15:34:38 +02:00
|
|
|
io->width = pic_hdr->width_;
|
|
|
|
io->height = pic_hdr->height_;
|
2011-06-20 09:45:15 +02:00
|
|
|
io->use_scaling = 0;
|
|
|
|
io->use_cropping = 0;
|
|
|
|
io->crop_top = 0;
|
|
|
|
io->crop_left = 0;
|
|
|
|
io->crop_right = io->width;
|
|
|
|
io->crop_bottom = io->height;
|
|
|
|
io->mb_w = io->width; // sanity check
|
|
|
|
io->mb_h = io->height; // ditto
|
2010-09-30 15:34:38 +02:00
|
|
|
|
|
|
|
VP8ResetProba(&dec->proba_);
|
|
|
|
ResetSegmentHeader(&dec->segment_hdr_);
|
|
|
|
dec->segment_ = 0; // default for intra
|
|
|
|
}
|
|
|
|
|
2011-03-11 00:05:59 +01:00
|
|
|
// Check if we have all the partition #0 available, and initialize dec->br_
|
|
|
|
// to read this partition (and this partition only).
|
2010-12-20 14:26:08 +01:00
|
|
|
if (frm_hdr->partition_length_ > buf_size) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"bad partition length");
|
2011-03-11 00:57:18 +01:00
|
|
|
}
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
|
2011-03-11 00:05:59 +01:00
|
|
|
br = &dec->br_;
|
|
|
|
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
|
2010-09-30 15:34:38 +02:00
|
|
|
buf += frm_hdr->partition_length_;
|
|
|
|
buf_size -= frm_hdr->partition_length_;
|
2011-03-11 00:05:59 +01:00
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
if (frm_hdr->key_frame_) {
|
|
|
|
pic_hdr->colorspace_ = VP8Get(br);
|
|
|
|
pic_hdr->clamp_type_ = VP8Get(br);
|
|
|
|
}
|
|
|
|
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"cannot parse segment header");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
// Filter specs
|
|
|
|
if (!ParseFilterHeader(br, dec)) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"cannot parse filter header");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2011-03-24 01:22:07 +01:00
|
|
|
status = ParsePartitions(dec, buf, buf_size);
|
|
|
|
if (status != VP8_STATUS_OK) {
|
|
|
|
return VP8SetError(dec, status, "cannot parse partitions");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// quantizer change
|
|
|
|
VP8ParseQuant(dec);
|
|
|
|
|
|
|
|
// Frame buffer marking
|
|
|
|
if (!frm_hdr->key_frame_) {
|
|
|
|
// Paragraph 9.7
|
|
|
|
#ifndef ONLY_KEYFRAME_CODE
|
|
|
|
dec->buffer_flags_ = VP8Get(br) << 0; // update golden
|
|
|
|
dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
|
|
|
|
if (!(dec->buffer_flags_ & 1)) {
|
|
|
|
dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
|
|
|
|
}
|
|
|
|
if (!(dec->buffer_flags_ & 2)) {
|
|
|
|
dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
|
|
|
|
}
|
|
|
|
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
|
|
|
|
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
|
|
|
|
#else
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
|
|
|
"Not a key frame.");
|
2010-09-30 15:34:38 +02:00
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
dec->buffer_flags_ = 0x003 | 0x100;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.8
|
2010-11-18 05:06:01 +01:00
|
|
|
#ifndef ONLY_KEYFRAME_CODE
|
2010-09-30 15:34:38 +02:00
|
|
|
dec->update_proba_ = VP8Get(br);
|
|
|
|
if (!dec->update_proba_) { // save for later restore
|
|
|
|
dec->proba_saved_ = dec->proba_;
|
|
|
|
}
|
|
|
|
dec->buffer_flags_ &= 1 << 8;
|
|
|
|
dec->buffer_flags_ |=
|
|
|
|
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
|
2010-11-18 05:06:01 +01:00
|
|
|
#else
|
|
|
|
VP8Get(br); // just ignore the value of update_proba_
|
2010-09-30 15:34:38 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
VP8ParseProba(br, dec);
|
|
|
|
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
|
|
// Extensions
|
|
|
|
if (dec->pic_hdr_.colorspace_) {
|
2011-05-03 02:19:00 +02:00
|
|
|
const size_t kTrailerSize = 8;
|
|
|
|
const uint8_t kTrailerMarker = 0x01;
|
2011-08-18 20:07:13 +02:00
|
|
|
const uint8_t* ext_buf = buf - kTrailerSize;
|
2011-05-03 02:19:00 +02:00
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (frm_hdr->partition_length_ < kTrailerSize ||
|
|
|
|
ext_buf[kTrailerSize - 1] != kTrailerMarker) {
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"RIFF: Inconsistent extra information.");
|
|
|
|
}
|
2011-05-03 02:19:00 +02:00
|
|
|
|
|
|
|
// Layer
|
|
|
|
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
|
|
|
|
dec->layer_data_size_ = size;
|
|
|
|
dec->layer_data_ = NULL; // will be set later
|
|
|
|
dec->layer_colorspace_ = ext_buf[3];
|
EXPERIMENTAL: add support for alpha channel
This is a (minor) bitstream change: if the 'color_space' bit is set to '1'
(which is normally an undefined/invalid behaviour), we add extra data at the
end of partition #0 (so-called 'extensions')
Namely, we add the size of the extension data as 3 bytes (little-endian),
followed by a set of bits telling which extensions we're incorporating.
The data then _preceeds_ this trailing tags.
This is all experimental, and you'll need to have
'#define WEBP_EXPERIMENTAL_FEATURES' in webp/types.h to enable this code
(at your own risk! :))
Still, this hack produces almost-valid WebP file for decoders that don't
check this color_space bit. In particular, previous 'dwebp' (and for instance
Chrome) will recognize this files and decode them, but without the alpha
of course. Other decoder will just see random extra stuff at the end of
partition #0.
To experiment with the alpha-channel, you need to compile on Unix platform
and use PNGs for input/output.
If 'alpha.png' is a source with alpha channel, then you can try (on Unix):
cwebp alpha.png -o alpha.webp
dwebp alpha.webp -o test.png
cwebp now has a '-noalpha' flag to ignore any alpha information from the
source, if present.
More hacking and experimenting welcome!
Change-Id: I3c7b1fd8411c9e7a9f77690e898479ad85c52f3e
2011-04-26 01:58:04 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
// sanitized state
|
|
|
|
dec->ready_ = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 15:34:38 +02:00
|
|
|
// Residual decoding (Paragraph 13.2 / 13.3)
|
|
|
|
|
|
|
|
static const uint8_t kBands[16 + 1] = {
|
|
|
|
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
|
|
0 // extra entry as sentinel
|
|
|
|
};
|
|
|
|
|
2010-11-26 18:53:26 +01:00
|
|
|
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
|
|
|
static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
|
|
|
|
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
2010-09-30 15:34:38 +02:00
|
|
|
static const uint8_t kCat6[] =
|
2010-11-26 18:53:26 +01:00
|
|
|
{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
|
2011-06-20 09:45:15 +02:00
|
|
|
static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
|
2010-09-30 15:34:38 +02:00
|
|
|
static const uint8_t kZigzag[16] = {
|
|
|
|
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
|
|
|
};
|
|
|
|
|
2010-11-26 18:53:26 +01:00
|
|
|
typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-03-14 20:27:12 +01:00
|
|
|
// Returns the position of the last non-zero coeff plus one
|
|
|
|
// (and 0 if there's no coeff at all)
|
2010-11-26 18:53:26 +01:00
|
|
|
static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
|
2012-01-30 02:38:37 +01:00
|
|
|
int ctx, const quant_t dq, int n, int16_t* out) {
|
2010-09-30 15:34:38 +02:00
|
|
|
const uint8_t* p = prob[kBands[n]][ctx];
|
|
|
|
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
|
2010-11-26 18:53:26 +01:00
|
|
|
return 0;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
while (1) {
|
|
|
|
++n;
|
|
|
|
if (!VP8GetBit(br, p[1])) {
|
|
|
|
p = prob[kBands[n]][0];
|
|
|
|
} else { // non zero coeff
|
2010-10-06 01:58:47 +02:00
|
|
|
int v, j;
|
2010-09-30 15:34:38 +02:00
|
|
|
if (!VP8GetBit(br, p[2])) {
|
|
|
|
p = prob[kBands[n]][1];
|
|
|
|
v = 1;
|
|
|
|
} else {
|
|
|
|
if (!VP8GetBit(br, p[3])) {
|
|
|
|
if (!VP8GetBit(br, p[4])) {
|
|
|
|
v = 2;
|
|
|
|
} else {
|
|
|
|
v = 3 + VP8GetBit(br, p[5]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!VP8GetBit(br, p[6])) {
|
|
|
|
if (!VP8GetBit(br, p[7])) {
|
|
|
|
v = 5 + VP8GetBit(br, 159);
|
|
|
|
} else {
|
2011-06-08 17:59:14 +02:00
|
|
|
v = 7 + 2 * VP8GetBit(br, 165);
|
|
|
|
v += VP8GetBit(br, 145);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
} else {
|
2010-11-26 18:53:26 +01:00
|
|
|
const uint8_t* tab;
|
2010-09-30 15:34:38 +02:00
|
|
|
const int bit1 = VP8GetBit(br, p[8]);
|
|
|
|
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
|
|
|
const int cat = 2 * bit1 + bit0;
|
|
|
|
v = 0;
|
2010-11-26 18:53:26 +01:00
|
|
|
for (tab = kCat3456[cat]; *tab; ++tab) {
|
2010-09-30 15:34:38 +02:00
|
|
|
v += v + VP8GetBit(br, *tab);
|
|
|
|
}
|
|
|
|
v += 3 + (8 << cat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p = prob[kBands[n]][2];
|
|
|
|
}
|
2010-10-06 01:58:47 +02:00
|
|
|
j = kZigzag[n - 1];
|
2010-09-30 15:34:38 +02:00
|
|
|
out[j] = VP8GetSigned(br, v) * dq[j > 0];
|
2010-11-26 18:53:26 +01:00
|
|
|
if (n == 16 || !VP8GetBit(br, p[0])) { // EOB
|
2011-03-14 20:27:12 +01:00
|
|
|
return n;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
2010-12-20 14:26:08 +01:00
|
|
|
if (n == 16) {
|
2011-03-14 20:27:12 +01:00
|
|
|
return 16;
|
2010-12-20 14:26:08 +01:00
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-10 06:29:36 +01:00
|
|
|
// Alias-safe way of converting 4bytes to 32bits.
|
2011-03-09 16:18:51 +01:00
|
|
|
typedef union {
|
|
|
|
uint8_t i8[4];
|
|
|
|
uint32_t i32;
|
|
|
|
} PackedNz;
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
// Table to unpack four bits into four bytes
|
2011-03-09 16:18:51 +01:00
|
|
|
static const PackedNz kUnpackTab[16] = {
|
|
|
|
{{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
|
|
|
|
{{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
|
|
|
|
{{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
|
|
|
|
{{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
|
2010-09-30 15:34:38 +02:00
|
|
|
|
|
|
|
// Macro to pack four LSB of four bytes into four bits.
|
2010-11-09 00:44:37 +01:00
|
|
|
#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
|
|
|
|
defined(__BIG_ENDIAN__)
|
|
|
|
#define PACK_CST 0x08040201U
|
|
|
|
#else
|
|
|
|
#define PACK_CST 0x01020408U
|
|
|
|
#endif
|
2011-03-09 16:18:51 +01:00
|
|
|
#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-03-10 06:29:36 +01:00
|
|
|
static void ParseResiduals(VP8Decoder* const dec,
|
|
|
|
VP8MB* const mb, VP8BitReader* const token_br) {
|
2010-09-30 15:34:38 +02:00
|
|
|
int out_t_nz, out_l_nz, first;
|
2010-11-26 18:53:26 +01:00
|
|
|
ProbaArray ac_prob;
|
2010-09-30 15:34:38 +02:00
|
|
|
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
|
|
|
|
int16_t* dst = dec->coeffs_;
|
|
|
|
VP8MB* const left_mb = dec->mb_info_ - 1;
|
2011-03-09 16:18:51 +01:00
|
|
|
PackedNz nz_ac, nz_dc;
|
|
|
|
PackedNz tnz, lnz;
|
2010-10-06 01:58:47 +02:00
|
|
|
uint32_t non_zero_ac = 0;
|
|
|
|
uint32_t non_zero_dc = 0;
|
|
|
|
int x, y, ch;
|
|
|
|
|
2010-09-30 15:34:38 +02:00
|
|
|
memset(dst, 0, 384 * sizeof(*dst));
|
|
|
|
if (!dec->is_i4x4_) { // parse DC
|
|
|
|
int16_t dc[16] = { 0 };
|
|
|
|
const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
|
2010-11-26 18:53:26 +01:00
|
|
|
mb->dc_nz_ = left_mb->dc_nz_ =
|
2011-03-14 20:27:12 +01:00
|
|
|
(GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
|
|
|
|
ctx, q->y2_mat_, 0, dc) > 0);
|
2010-09-30 15:34:38 +02:00
|
|
|
first = 1;
|
2010-11-26 18:53:26 +01:00
|
|
|
ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
|
2010-09-30 15:34:38 +02:00
|
|
|
VP8TransformWHT(dc, dst);
|
|
|
|
} else {
|
|
|
|
first = 0;
|
2010-11-26 18:53:26 +01:00
|
|
|
ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
2011-03-09 16:18:51 +01:00
|
|
|
tnz = kUnpackTab[mb->nz_ & 0xf];
|
|
|
|
lnz = kUnpackTab[left_mb->nz_ & 0xf];
|
2010-10-06 01:58:47 +02:00
|
|
|
for (y = 0; y < 4; ++y) {
|
2011-03-09 16:18:51 +01:00
|
|
|
int l = lnz.i8[y];
|
2010-10-06 01:58:47 +02:00
|
|
|
for (x = 0; x < 4; ++x) {
|
2011-03-09 16:18:51 +01:00
|
|
|
const int ctx = l + tnz.i8[x];
|
2011-03-14 20:27:12 +01:00
|
|
|
const int nz = GetCoeffs(token_br, ac_prob, ctx,
|
|
|
|
q->y1_mat_, first, dst);
|
|
|
|
tnz.i8[x] = l = (nz > 0);
|
2011-03-09 16:18:51 +01:00
|
|
|
nz_dc.i8[x] = (dst[0] != 0);
|
2011-03-14 20:27:12 +01:00
|
|
|
nz_ac.i8[x] = (nz > 1);
|
2010-09-30 15:34:38 +02:00
|
|
|
dst += 16;
|
|
|
|
}
|
2011-03-09 16:18:51 +01:00
|
|
|
lnz.i8[y] = l;
|
2010-09-30 15:34:38 +02:00
|
|
|
non_zero_dc |= PACK(nz_dc, 24 - y * 4);
|
|
|
|
non_zero_ac |= PACK(nz_ac, 24 - y * 4);
|
|
|
|
}
|
|
|
|
out_t_nz = PACK(tnz, 24);
|
|
|
|
out_l_nz = PACK(lnz, 24);
|
|
|
|
|
2011-03-09 16:18:51 +01:00
|
|
|
tnz = kUnpackTab[mb->nz_ >> 4];
|
|
|
|
lnz = kUnpackTab[left_mb->nz_ >> 4];
|
2010-10-06 01:58:47 +02:00
|
|
|
for (ch = 0; ch < 4; ch += 2) {
|
|
|
|
for (y = 0; y < 2; ++y) {
|
2011-03-09 16:18:51 +01:00
|
|
|
int l = lnz.i8[ch + y];
|
2010-10-06 01:58:47 +02:00
|
|
|
for (x = 0; x < 2; ++x) {
|
2011-03-09 16:18:51 +01:00
|
|
|
const int ctx = l + tnz.i8[ch + x];
|
2011-03-14 20:27:12 +01:00
|
|
|
const int nz =
|
|
|
|
GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
|
2010-09-30 15:34:38 +02:00
|
|
|
ctx, q->uv_mat_, 0, dst);
|
2011-03-14 20:27:12 +01:00
|
|
|
tnz.i8[ch + x] = l = (nz > 0);
|
2011-03-09 16:18:51 +01:00
|
|
|
nz_dc.i8[y * 2 + x] = (dst[0] != 0);
|
2011-03-14 20:27:12 +01:00
|
|
|
nz_ac.i8[y * 2 + x] = (nz > 1);
|
2010-09-30 15:34:38 +02:00
|
|
|
dst += 16;
|
|
|
|
}
|
2011-03-09 16:18:51 +01:00
|
|
|
lnz.i8[ch + y] = l;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
|
|
|
|
non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
|
|
|
|
}
|
|
|
|
out_t_nz |= PACK(tnz, 20);
|
|
|
|
out_l_nz |= PACK(lnz, 20);
|
|
|
|
mb->nz_ = out_t_nz;
|
|
|
|
left_mb->nz_ = out_l_nz;
|
|
|
|
|
|
|
|
dec->non_zero_ac_ = non_zero_ac;
|
|
|
|
dec->non_zero_ = non_zero_ac | non_zero_dc;
|
|
|
|
mb->skip_ = !dec->non_zero_;
|
|
|
|
}
|
|
|
|
#undef PACK
|
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 15:34:38 +02:00
|
|
|
// Main loop
|
|
|
|
|
2011-03-10 06:29:36 +01:00
|
|
|
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
2010-09-30 15:34:38 +02:00
|
|
|
VP8BitReader* const br = &dec->br_;
|
2011-03-10 06:29:36 +01:00
|
|
|
VP8MB* const left = dec->mb_info_ - 1;
|
|
|
|
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
|
|
|
|
|
|
|
// Note: we don't save segment map (yet), as we don't expect
|
|
|
|
// to decode more than 1 keyframe.
|
|
|
|
if (dec->segment_hdr_.update_map_) {
|
|
|
|
// Hardcoded tree parsing
|
|
|
|
dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
|
|
|
|
VP8GetBit(br, dec->proba_.segments_[1]) :
|
|
|
|
2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
|
|
|
}
|
|
|
|
info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
|
|
|
|
|
|
|
|
VP8ParseIntraMode(br, dec);
|
|
|
|
if (br->eof_) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info->skip_) {
|
|
|
|
ParseResiduals(dec, info, token_br);
|
|
|
|
} else {
|
|
|
|
left->nz_ = info->nz_ = 0;
|
|
|
|
if (!dec->is_i4x4_) {
|
|
|
|
left->dc_nz_ = info->dc_nz_ = 0;
|
|
|
|
}
|
|
|
|
dec->non_zero_ = 0;
|
|
|
|
dec->non_zero_ac_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (!token_br->eof_);
|
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
|
2011-07-22 22:09:10 +02:00
|
|
|
void VP8InitScanline(VP8Decoder* const dec) {
|
|
|
|
VP8MB* const left = dec->mb_info_ - 1;
|
|
|
|
left->nz_ = 0;
|
|
|
|
left->dc_nz_ = 0;
|
|
|
|
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
|
|
|
dec->filter_row_ =
|
|
|
|
(dec->filter_type_ > 0) &&
|
|
|
|
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
|
|
|
}
|
|
|
|
|
2011-03-10 06:29:36 +01:00
|
|
|
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
2011-06-20 09:45:15 +02:00
|
|
|
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
2011-03-10 06:29:36 +01:00
|
|
|
VP8BitReader* const token_br =
|
|
|
|
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
2011-07-22 22:09:10 +02:00
|
|
|
VP8InitScanline(dec);
|
2010-09-30 15:34:38 +02:00
|
|
|
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
2011-03-10 06:29:36 +01:00
|
|
|
if (!VP8DecodeMB(dec, token_br)) {
|
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"Premature end-of-file encountered.");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
VP8ReconstructBlock(dec);
|
|
|
|
|
2010-11-03 22:27:51 +01:00
|
|
|
// Store data and save block's filtering params
|
2010-11-04 01:52:00 +01:00
|
|
|
VP8StoreBlock(dec);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
2011-07-22 22:09:10 +02:00
|
|
|
if (!VP8ProcessRow(dec, io)) {
|
2011-06-20 09:45:15 +02:00
|
|
|
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
2011-07-22 22:09:10 +02:00
|
|
|
if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
|
|
|
|
// Finish
|
2010-11-18 05:06:01 +01:00
|
|
|
#ifndef ONLY_KEYFRAME_CODE
|
2010-09-30 15:34:38 +02:00
|
|
|
if (!dec->update_proba_) {
|
|
|
|
dec->proba_ = dec->proba_saved_;
|
|
|
|
}
|
2010-11-18 05:06:01 +01:00
|
|
|
#endif
|
|
|
|
|
2011-05-03 02:19:00 +02:00
|
|
|
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
|
|
|
if (dec->layer_data_size_ > 0) {
|
|
|
|
if (!VP8DecodeLayer(dec)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-02-16 23:33:16 +01:00
|
|
|
return 1;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Main entry point
|
|
|
|
int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
2011-07-22 22:09:10 +02:00
|
|
|
int ok = 0;
|
2010-09-30 15:34:38 +02:00
|
|
|
if (dec == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (io == NULL) {
|
2011-02-16 23:33:16 +01:00
|
|
|
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
|
|
|
"NULL VP8Io parameter in VP8Decode().");
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dec->ready_) {
|
|
|
|
if (!VP8GetHeaders(dec, io)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(dec->ready_);
|
|
|
|
|
2011-07-22 22:09:10 +02:00
|
|
|
// Finish setting up the decoding parameter. Will call io->setup().
|
|
|
|
ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
|
|
|
|
if (ok) { // good to go.
|
|
|
|
// Will allocate memory and prepare everything.
|
|
|
|
if (ok) ok = VP8InitFrame(dec, io);
|
|
|
|
|
|
|
|
// Main decoding loop
|
|
|
|
if (ok) ok = ParseFrame(dec, io);
|
|
|
|
|
|
|
|
// Exit.
|
|
|
|
ok &= VP8ExitCritical(dec, io);
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
2011-07-22 22:09:10 +02:00
|
|
|
if (!ok) {
|
2010-09-30 15:34:38 +02:00
|
|
|
VP8Clear(dec);
|
2011-06-20 09:45:15 +02:00
|
|
|
return 0;
|
2010-09-30 15:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dec->ready_ = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VP8Clear(VP8Decoder* const dec) {
|
|
|
|
if (dec == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
2011-07-22 22:09:10 +02:00
|
|
|
if (dec->use_threads_) {
|
|
|
|
WebPWorkerEnd(&dec->worker_);
|
|
|
|
}
|
2010-09-30 15:34:38 +02:00
|
|
|
if (dec->mem_) {
|
|
|
|
free(dec->mem_);
|
|
|
|
}
|
|
|
|
dec->mem_ = NULL;
|
|
|
|
dec->mem_size_ = 0;
|
|
|
|
memset(&dec->br_, 0, sizeof(dec->br_));
|
|
|
|
dec->ready_ = 0;
|
|
|
|
}
|
2011-03-10 06:25:53 +01:00
|
|
|
|
2011-08-25 23:22:32 +02:00
|
|
|
//------------------------------------------------------------------------------
|
2011-03-10 06:25:53 +01:00
|
|
|
|
|
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
|
|
|
} // extern "C"
|
|
|
|
#endif
|