2012-01-06 14:49:06 -08:00
|
|
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
2010-09-30 09:34:38 -04:00
|
|
|
//
|
2013-06-06 23:05:58 -07:00
|
|
|
// Use of this source code is governed by a BSD-style license
|
|
|
|
// that can be found in the COPYING file in the root of the source
|
|
|
|
// tree. An additional intellectual property rights grant can be found
|
|
|
|
// in the file PATENTS. All contributing project authors may
|
|
|
|
// be found in the AUTHORS file in the root of the source tree.
|
2010-09-30 09:34:38 -04:00
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// main entry for the decoder
|
|
|
|
//
|
|
|
|
// Author: Skal (pascal.massimino@gmail.com)
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2011-12-01 15:30:03 +05:30
|
|
|
|
2017-10-07 14:15:11 -07:00
|
|
|
#include "src/dec/alphai_dec.h"
|
|
|
|
#include "src/dec/vp8i_dec.h"
|
|
|
|
#include "src/dec/vp8li_dec.h"
|
|
|
|
#include "src/dec/webpi_dec.h"
|
|
|
|
#include "src/utils/bit_reader_inl_utils.h"
|
|
|
|
#include "src/utils/utils.h"
|
2010-09-30 09:34:38 -04:00
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2011-03-24 16:17:10 -07:00
|
|
|
|
2011-03-25 15:04:11 -07:00
|
|
|
int WebPGetDecoderVersion(void) {
|
2011-03-24 16:17:10 -07:00
|
|
|
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
|
|
|
}
|
|
|
|
|
2017-01-13 16:35:42 +01:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Signature and pointer-to-function for GetCoeffs() variants below.
|
|
|
|
|
|
|
|
typedef int (*GetCoeffsFunc)(VP8BitReader* const br,
|
|
|
|
const VP8BandProbas* const prob[],
|
|
|
|
int ctx, const quant_t dq, int n, int16_t* out);
|
|
|
|
static volatile GetCoeffsFunc GetCoeffs = NULL;
|
|
|
|
|
|
|
|
static void InitGetCoeffs(void);
|
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 09:34:38 -04:00
|
|
|
// VP8Decoder
|
|
|
|
|
|
|
|
static void SetOk(VP8Decoder* const dec) {
|
2011-02-16 14:33:16 -08:00
|
|
|
dec->status_ = VP8_STATUS_OK;
|
2010-09-30 09:34:38 -04:00
|
|
|
dec->error_msg_ = "OK";
|
|
|
|
}
|
|
|
|
|
2011-02-27 10:51:01 -08:00
|
|
|
int VP8InitIoInternal(VP8Io* const io, int version) {
|
2012-07-18 11:53:25 -07:00
|
|
|
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
2011-02-27 10:51:01 -08:00
|
|
|
return 0; // mismatch error
|
2012-07-18 11:53:25 -07:00
|
|
|
}
|
|
|
|
if (io != NULL) {
|
2010-09-30 09:34:38 -04:00
|
|
|
memset(io, 0, sizeof(*io));
|
|
|
|
}
|
2011-02-27 10:51:01 -08:00
|
|
|
return 1;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2011-03-25 15:04:11 -07:00
|
|
|
VP8Decoder* VP8New(void) {
|
2014-03-27 23:27:32 +01:00
|
|
|
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
2012-07-18 11:53:25 -07:00
|
|
|
if (dec != NULL) {
|
2010-09-30 09:34:38 -04:00
|
|
|
SetOk(dec);
|
2014-06-12 11:35:44 +02:00
|
|
|
WebPGetWorkerInterface()->Init(&dec->worker_);
|
2010-09-30 09:34:38 -04:00
|
|
|
dec->ready_ = 0;
|
2016-04-30 00:16:59 -07:00
|
|
|
dec->num_parts_minus_one_ = 0;
|
2017-01-13 16:35:42 +01:00
|
|
|
InitGetCoeffs();
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
return dec;
|
|
|
|
}
|
|
|
|
|
2011-02-16 14:33:16 -08:00
|
|
|
VP8StatusCode VP8Status(VP8Decoder* const dec) {
|
|
|
|
if (!dec) return VP8_STATUS_INVALID_PARAM;
|
2010-09-30 09:34:38 -04:00
|
|
|
return dec->status_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* VP8StatusMessage(VP8Decoder* const dec) {
|
2012-07-18 11:53:25 -07:00
|
|
|
if (dec == NULL) return "no object";
|
2010-09-30 09:34:38 -04:00
|
|
|
if (!dec->error_msg_) return "OK";
|
|
|
|
return dec->error_msg_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VP8Delete(VP8Decoder* const dec) {
|
2012-07-18 11:53:25 -07:00
|
|
|
if (dec != NULL) {
|
2010-09-30 09:34:38 -04:00
|
|
|
VP8Clear(dec);
|
2014-03-27 23:27:32 +01:00
|
|
|
WebPSafeFree(dec);
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-16 14:33:16 -08:00
|
|
|
int VP8SetError(VP8Decoder* const dec,
|
2012-05-03 11:58:34 -07:00
|
|
|
VP8StatusCode error, const char* const msg) {
|
2015-04-01 00:32:52 -07:00
|
|
|
// The oldest error reported takes precedence over the new one.
|
2012-06-13 14:30:56 -07:00
|
|
|
if (dec->status_ == VP8_STATUS_OK) {
|
|
|
|
dec->status_ = error;
|
|
|
|
dec->error_msg_ = msg;
|
|
|
|
dec->ready_ = 0;
|
|
|
|
}
|
2010-09-30 09:34:38 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2011-05-19 19:07:01 -07:00
|
|
|
|
2012-05-31 11:16:32 +05:30
|
|
|
int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
|
|
|
|
return (data_size >= 3 &&
|
|
|
|
data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
|
|
|
|
}
|
|
|
|
|
2012-04-12 13:06:54 -07:00
|
|
|
int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
2012-04-24 18:04:38 -07:00
|
|
|
int* const width, int* const height) {
|
2012-05-02 12:38:10 -07:00
|
|
|
if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
|
2011-06-20 00:45:15 -07:00
|
|
|
return 0; // not enough data
|
|
|
|
}
|
2011-05-19 19:07:01 -07:00
|
|
|
// check signature
|
2012-05-31 11:16:32 +05:30
|
|
|
if (!VP8CheckSignature(data + 3, data_size - 3)) {
|
2011-05-19 19:07:01 -07:00
|
|
|
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.
|
|
|
|
}
|
2013-04-25 12:08:40 -07:00
|
|
|
if (w == 0 || h == 0) {
|
|
|
|
return 0; // We don't support both width and height to be zero.
|
|
|
|
}
|
2011-05-19 19:07:01 -07:00
|
|
|
|
|
|
|
if (width) {
|
|
|
|
*width = w;
|
|
|
|
}
|
|
|
|
if (height) {
|
|
|
|
*height = h;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 09:34:38 -04:00
|
|
|
// Header parsing
|
|
|
|
|
|
|
|
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
|
2012-07-18 11:53:25 -07:00
|
|
|
assert(hdr != NULL);
|
2010-09-30 09:34:38 -04:00
|
|
|
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) {
|
2012-07-18 11:53:25 -07:00
|
|
|
assert(br != NULL);
|
|
|
|
assert(hdr != NULL);
|
2010-09-30 09:34:38 -04:00
|
|
|
hdr->use_segment_ = VP8Get(br);
|
|
|
|
if (hdr->use_segment_) {
|
|
|
|
hdr->update_map_ = VP8Get(br);
|
2010-10-05 16:58:47 -07:00
|
|
|
if (VP8Get(br)) { // update data
|
|
|
|
int s;
|
2010-09-30 09:34:38 -04:00
|
|
|
hdr->absolute_delta_ = VP8Get(br);
|
2010-10-05 16:58:47 -07:00
|
|
|
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
2010-09-30 09:34:38 -04:00
|
|
|
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
|
|
|
|
}
|
2010-10-05 16:58:47 -07:00
|
|
|
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
2010-09-30 09:34:38 -04:00
|
|
|
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hdr->update_map_) {
|
2010-10-05 16:58:47 -07:00
|
|
|
int s;
|
|
|
|
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
2010-09-30 09:34:38 -04:00
|
|
|
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hdr->update_map_ = 0;
|
|
|
|
}
|
2011-02-27 10:51:01 -08:00
|
|
|
return !br->eof_;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.5
|
2011-03-10 15:05:59 -08: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,
|
2012-04-12 13:06:54 -07:00
|
|
|
const uint8_t* buf, size_t size) {
|
2010-09-30 09:34:38 -04:00
|
|
|
VP8BitReader* const br = &dec->br_;
|
|
|
|
const uint8_t* sz = buf;
|
2011-03-10 15:05:59 -08:00
|
|
|
const uint8_t* buf_end = buf + size;
|
|
|
|
const uint8_t* part_start;
|
2015-11-20 09:12:48 +00:00
|
|
|
size_t size_left = size;
|
|
|
|
size_t last_part;
|
|
|
|
size_t p;
|
2010-10-05 16:58:47 -07:00
|
|
|
|
2016-04-30 00:16:59 -07:00
|
|
|
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1;
|
|
|
|
last_part = dec->num_parts_minus_one_;
|
2015-11-20 09:12:48 +00:00
|
|
|
if (size < 3 * last_part) {
|
2011-03-10 15:05:59 -08:00
|
|
|
// we can't even read the sizes with sz[]! That's a failure.
|
|
|
|
return VP8_STATUS_NOT_ENOUGH_DATA;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2015-11-20 09:12:48 +00:00
|
|
|
part_start = buf + last_part * 3;
|
|
|
|
size_left -= last_part * 3;
|
2010-10-05 16:58:47 -07:00
|
|
|
for (p = 0; p < last_part; ++p) {
|
2015-11-20 09:12:48 +00:00
|
|
|
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
|
|
|
if (psize > size_left) psize = size_left;
|
|
|
|
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
|
|
|
part_start += psize;
|
|
|
|
size_left -= psize;
|
2010-09-30 09:34:38 -04:00
|
|
|
sz += 3;
|
|
|
|
}
|
2015-11-20 09:12:48 +00:00
|
|
|
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
2011-03-10 15:05:59 -08:00
|
|
|
return (part_start < buf_end) ? VP8_STATUS_OK :
|
|
|
|
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
2010-09-30 09:34:38 -04: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-05 16:58:47 -07:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
2010-09-30 09:34:38 -04:00
|
|
|
if (VP8Get(br)) {
|
|
|
|
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
|
|
|
}
|
|
|
|
}
|
2010-10-05 16:58:47 -07:00
|
|
|
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
2010-09-30 09:34:38 -04:00
|
|
|
if (VP8Get(br)) {
|
|
|
|
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
2011-02-27 10:51:01 -08:00
|
|
|
return !br->eof_;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Topmost call
|
|
|
|
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
2011-08-18 11:07:13 -07:00
|
|
|
const uint8_t* buf;
|
2012-04-12 13:06:54 -07:00
|
|
|
size_t buf_size;
|
2010-10-05 16:58:47 -07:00
|
|
|
VP8FrameHeader* frm_hdr;
|
|
|
|
VP8PictureHeader* pic_hdr;
|
|
|
|
VP8BitReader* br;
|
2011-03-23 17:22:07 -07:00
|
|
|
VP8StatusCode status;
|
2010-10-05 16:58:47 -07:00
|
|
|
|
2010-09-30 09:34:38 -04:00
|
|
|
if (dec == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
SetOk(dec);
|
2010-12-20 05:45:49 -08:00
|
|
|
if (io == NULL) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
|
|
|
"null VP8Io passed to VP8GetHeaders()");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2013-04-11 11:35:18 -07:00
|
|
|
buf = io->data;
|
|
|
|
buf_size = io->data_size;
|
2011-08-18 11:07:13 -07:00
|
|
|
if (buf_size < 4) {
|
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
2011-09-13 09:35:10 +00:00
|
|
|
"Truncated header.");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paragraph 9.1
|
2010-10-05 16:58:47 -07:00
|
|
|
{
|
|
|
|
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
2010-10-19 17:16:49 -04:00
|
|
|
frm_hdr = &dec->frm_hdr_;
|
2010-10-05 16:58:47 -07: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);
|
2016-08-03 18:53:40 -07:00
|
|
|
if (frm_hdr->profile_ > 3) {
|
2011-02-27 10:51:01 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"Incorrect keyframe parameters.");
|
2016-08-03 18:53:40 -07:00
|
|
|
}
|
|
|
|
if (!frm_hdr->show_) {
|
2011-02-27 10:51:01 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
|
|
|
"Frame not displayable.");
|
2016-08-03 18:53:40 -07:00
|
|
|
}
|
2010-10-05 16:58:47 -07:00
|
|
|
buf += 3;
|
|
|
|
buf_size -= 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
pic_hdr = &dec->pic_hdr_;
|
2010-09-30 09:34:38 -04:00
|
|
|
if (frm_hdr->key_frame_) {
|
|
|
|
// Paragraph 9.2
|
|
|
|
if (buf_size < 7) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"cannot parse picture header");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2012-05-31 11:16:32 +05:30
|
|
|
if (!VP8CheckSignature(buf, buf_size)) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"Bad code word");
|
2010-09-30 09:34:38 -04: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;
|
2016-04-03 23:57:20 -07:00
|
|
|
|
2011-06-20 00:45:15 -07:00
|
|
|
// Setup default output area (can be later modified during io->setup())
|
2010-09-30 09:34:38 -04:00
|
|
|
io->width = pic_hdr->width_;
|
|
|
|
io->height = pic_hdr->height_;
|
2016-04-03 23:57:20 -07:00
|
|
|
// IMPORTANT! use some sane dimensions in crop_* and scaled_* fields.
|
|
|
|
// So they can be used interchangeably without always testing for
|
|
|
|
// 'use_cropping'.
|
2011-06-20 00:45:15 -07:00
|
|
|
io->use_cropping = 0;
|
|
|
|
io->crop_top = 0;
|
|
|
|
io->crop_left = 0;
|
|
|
|
io->crop_right = io->width;
|
|
|
|
io->crop_bottom = io->height;
|
2016-04-03 23:57:20 -07:00
|
|
|
io->use_scaling = 0;
|
|
|
|
io->scaled_width = io->width;
|
|
|
|
io->scaled_height = io->height;
|
|
|
|
|
2011-06-20 00:45:15 -07:00
|
|
|
io->mb_w = io->width; // sanity check
|
|
|
|
io->mb_h = io->height; // ditto
|
2010-09-30 09:34:38 -04:00
|
|
|
|
|
|
|
VP8ResetProba(&dec->proba_);
|
|
|
|
ResetSegmentHeader(&dec->segment_hdr_);
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:05:59 -08: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 05:26:08 -08:00
|
|
|
if (frm_hdr->partition_length_ > buf_size) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"bad partition length");
|
2011-03-10 15:57:18 -08: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-25 16:58:04 -07:00
|
|
|
|
2011-03-10 15:05:59 -08:00
|
|
|
br = &dec->br_;
|
2015-11-20 09:12:48 +00:00
|
|
|
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
2010-09-30 09:34:38 -04:00
|
|
|
buf += frm_hdr->partition_length_;
|
|
|
|
buf_size -= frm_hdr->partition_length_;
|
2011-03-10 15:05:59 -08:00
|
|
|
|
2010-09-30 09:34:38 -04: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 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"cannot parse segment header");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
// Filter specs
|
|
|
|
if (!ParseFilterHeader(br, dec)) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
|
|
|
"cannot parse filter header");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2011-03-23 17:22:07 -07:00
|
|
|
status = ParsePartitions(dec, buf, buf_size);
|
|
|
|
if (status != VP8_STATUS_OK) {
|
|
|
|
return VP8SetError(dec, status, "cannot parse partitions");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// quantizer change
|
|
|
|
VP8ParseQuant(dec);
|
|
|
|
|
|
|
|
// Frame buffer marking
|
|
|
|
if (!frm_hdr->key_frame_) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
|
|
|
"Not a key frame.");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2013-11-04 11:52:05 +01:00
|
|
|
VP8Get(br); // ignore the value of update_proba_
|
2010-09-30 09:34:38 -04:00
|
|
|
|
|
|
|
VP8ParseProba(br, dec);
|
|
|
|
|
|
|
|
// sanitized state
|
|
|
|
dec->ready_ = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 09:34:38 -04:00
|
|
|
// Residual decoding (Paragraph 13.2 / 13.3)
|
|
|
|
|
2010-11-26 09:53:26 -08: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 09:34:38 -04:00
|
|
|
static const uint8_t kCat6[] =
|
2010-11-26 09:53:26 -08:00
|
|
|
{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
|
2011-06-20 00:45:15 -07:00
|
|
|
static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
|
2010-09-30 09:34:38 -04:00
|
|
|
static const uint8_t kZigzag[16] = {
|
|
|
|
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
|
|
|
};
|
|
|
|
|
2012-11-28 08:24:23 +01:00
|
|
|
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
|
|
|
|
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
|
|
|
int v;
|
|
|
|
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 {
|
|
|
|
v = 7 + 2 * VP8GetBit(br, 165);
|
|
|
|
v += VP8GetBit(br, 145);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const uint8_t* tab;
|
|
|
|
const int bit1 = VP8GetBit(br, p[8]);
|
|
|
|
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
|
|
|
const int cat = 2 * bit1 + bit0;
|
|
|
|
v = 0;
|
|
|
|
for (tab = kCat3456[cat]; *tab; ++tab) {
|
|
|
|
v += v + VP8GetBit(br, *tab);
|
|
|
|
}
|
|
|
|
v += 3 + (8 << cat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
2010-09-30 09:34:38 -04:00
|
|
|
|
2011-03-14 12:27:12 -07:00
|
|
|
// Returns the position of the last non-zero coeff plus one
|
2017-01-13 16:35:42 +01:00
|
|
|
static int GetCoeffsFast(VP8BitReader* const br,
|
|
|
|
const VP8BandProbas* const prob[],
|
|
|
|
int ctx, const quant_t dq, int n, int16_t* out) {
|
2014-12-11 08:06:20 +01:00
|
|
|
const uint8_t* p = prob[n]->probas_[ctx];
|
2012-11-28 08:24:23 +01:00
|
|
|
for (; n < 16; ++n) {
|
2013-09-30 23:11:51 +02:00
|
|
|
if (!VP8GetBit(br, p[0])) {
|
|
|
|
return n; // previous coeff was last non-zero coeff
|
|
|
|
}
|
|
|
|
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
|
2014-12-11 08:06:20 +01:00
|
|
|
p = prob[++n]->probas_[0];
|
2013-09-30 23:11:51 +02:00
|
|
|
if (n == 16) return 16;
|
|
|
|
}
|
|
|
|
{ // non zero coeff
|
2014-12-11 08:06:20 +01:00
|
|
|
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
2012-11-28 08:24:23 +01:00
|
|
|
int v;
|
2010-09-30 09:34:38 -04:00
|
|
|
if (!VP8GetBit(br, p[2])) {
|
|
|
|
v = 1;
|
2012-11-28 08:24:23 +01:00
|
|
|
p = p_ctx[1];
|
2010-09-30 09:34:38 -04:00
|
|
|
} else {
|
2012-11-28 08:24:23 +01:00
|
|
|
v = GetLargeValue(br, p);
|
|
|
|
p = p_ctx[2];
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2012-11-28 08:24:23 +01:00
|
|
|
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
}
|
2012-11-28 08:24:23 +01:00
|
|
|
return 16;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2017-01-13 16:35:42 +01:00
|
|
|
// This version of GetCoeffs() uses VP8GetBitAlt() which is an alternate version
|
|
|
|
// of VP8GetBitAlt() targeting specific platforms.
|
|
|
|
static int GetCoeffsAlt(VP8BitReader* const br,
|
|
|
|
const VP8BandProbas* const prob[],
|
|
|
|
int ctx, const quant_t dq, int n, int16_t* out) {
|
|
|
|
const uint8_t* p = prob[n]->probas_[ctx];
|
|
|
|
for (; n < 16; ++n) {
|
|
|
|
if (!VP8GetBitAlt(br, p[0])) {
|
|
|
|
return n; // previous coeff was last non-zero coeff
|
|
|
|
}
|
|
|
|
while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs
|
|
|
|
p = prob[++n]->probas_[0];
|
|
|
|
if (n == 16) return 16;
|
|
|
|
}
|
|
|
|
{ // non zero coeff
|
|
|
|
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
|
|
|
int v;
|
|
|
|
if (!VP8GetBitAlt(br, p[2])) {
|
|
|
|
v = 1;
|
|
|
|
p = p_ctx[1];
|
|
|
|
} else {
|
|
|
|
v = GetLargeValue(br, p);
|
|
|
|
p = p_ctx[2];
|
|
|
|
}
|
|
|
|
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
WEBP_TSAN_IGNORE_FUNCTION static void InitGetCoeffs(void) {
|
|
|
|
if (GetCoeffs == NULL) {
|
|
|
|
if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
|
|
|
|
GetCoeffs = GetCoeffsAlt;
|
|
|
|
} else {
|
|
|
|
GetCoeffs = GetCoeffsFast;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-09 11:43:19 +02:00
|
|
|
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
|
|
|
nz_coeffs <<= 2;
|
|
|
|
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
|
|
|
return nz_coeffs;
|
|
|
|
}
|
|
|
|
|
2013-05-15 20:03:15 +02:00
|
|
|
static int ParseResiduals(VP8Decoder* const dec,
|
|
|
|
VP8MB* const mb, VP8BitReader* const token_br) {
|
2014-12-11 08:06:20 +01:00
|
|
|
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
|
|
|
const VP8BandProbas* const * ac_proba;
|
2013-10-10 21:29:58 +02:00
|
|
|
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
2014-01-28 21:40:40 +01:00
|
|
|
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
2013-05-31 22:38:12 +02:00
|
|
|
int16_t* dst = block->coeffs_;
|
2010-09-30 09:34:38 -04:00
|
|
|
VP8MB* const left_mb = dec->mb_info_ - 1;
|
2013-09-05 08:53:36 +02:00
|
|
|
uint8_t tnz, lnz;
|
2013-10-08 22:05:38 +02:00
|
|
|
uint32_t non_zero_y = 0;
|
|
|
|
uint32_t non_zero_uv = 0;
|
2010-10-05 16:58:47 -07:00
|
|
|
int x, y, ch;
|
2013-09-13 02:03:11 -07:00
|
|
|
uint32_t out_t_nz, out_l_nz;
|
|
|
|
int first;
|
2010-10-05 16:58:47 -07:00
|
|
|
|
2010-09-30 09:34:38 -04:00
|
|
|
memset(dst, 0, 384 * sizeof(*dst));
|
2013-05-31 22:38:12 +02:00
|
|
|
if (!block->is_i4x4_) { // parse DC
|
2010-09-30 09:34:38 -04:00
|
|
|
int16_t dc[16] = { 0 };
|
2013-05-15 20:03:15 +02:00
|
|
|
const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
|
2013-10-02 14:27:36 +02:00
|
|
|
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
|
|
|
|
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
|
|
|
|
if (nz > 1) { // more than just the DC -> perform the full transform
|
|
|
|
VP8TransformWHT(dc, dst);
|
|
|
|
} else { // only DC is non-zero -> inlined simplified transform
|
|
|
|
int i;
|
|
|
|
const int dc0 = (dc[0] + 3) >> 3;
|
|
|
|
for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
|
|
|
|
}
|
2013-09-13 02:03:11 -07:00
|
|
|
first = 1;
|
|
|
|
ac_proba = bands[0];
|
2010-09-30 09:34:38 -04:00
|
|
|
} else {
|
|
|
|
first = 0;
|
2013-09-13 02:03:11 -07:00
|
|
|
ac_proba = bands[3];
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2013-09-05 08:53:36 +02:00
|
|
|
tnz = mb->nz_ & 0x0f;
|
|
|
|
lnz = left_mb->nz_ & 0x0f;
|
2010-10-05 16:58:47 -07:00
|
|
|
for (y = 0; y < 4; ++y) {
|
2013-09-05 08:53:36 +02:00
|
|
|
int l = lnz & 1;
|
2013-10-08 22:05:38 +02:00
|
|
|
uint32_t nz_coeffs = 0;
|
2010-10-05 16:58:47 -07:00
|
|
|
for (x = 0; x < 4; ++x) {
|
2013-09-05 08:53:36 +02:00
|
|
|
const int ctx = l + (tnz & 1);
|
2013-09-13 02:03:11 -07:00
|
|
|
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
|
2013-09-30 23:11:51 +02:00
|
|
|
l = (nz > first);
|
2013-09-05 08:53:36 +02:00
|
|
|
tnz = (tnz >> 1) | (l << 7);
|
2013-10-09 11:43:19 +02:00
|
|
|
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
2010-09-30 09:34:38 -04:00
|
|
|
dst += 16;
|
|
|
|
}
|
2013-09-05 08:53:36 +02:00
|
|
|
tnz >>= 4;
|
|
|
|
lnz = (lnz >> 1) | (l << 7);
|
2013-10-08 22:05:38 +02:00
|
|
|
non_zero_y = (non_zero_y << 8) | nz_coeffs;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2013-09-05 08:53:36 +02:00
|
|
|
out_t_nz = tnz;
|
|
|
|
out_l_nz = lnz >> 4;
|
2010-09-30 09:34:38 -04:00
|
|
|
|
2010-10-05 16:58:47 -07:00
|
|
|
for (ch = 0; ch < 4; ch += 2) {
|
2013-10-08 22:05:38 +02:00
|
|
|
uint32_t nz_coeffs = 0;
|
2013-09-05 08:53:36 +02:00
|
|
|
tnz = mb->nz_ >> (4 + ch);
|
|
|
|
lnz = left_mb->nz_ >> (4 + ch);
|
2010-10-05 16:58:47 -07:00
|
|
|
for (y = 0; y < 2; ++y) {
|
2013-09-05 08:53:36 +02:00
|
|
|
int l = lnz & 1;
|
2010-10-05 16:58:47 -07:00
|
|
|
for (x = 0; x < 2; ++x) {
|
2013-09-05 08:53:36 +02:00
|
|
|
const int ctx = l + (tnz & 1);
|
2013-09-13 02:03:11 -07:00
|
|
|
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
|
2013-09-05 08:53:36 +02:00
|
|
|
l = (nz > 0);
|
|
|
|
tnz = (tnz >> 1) | (l << 3);
|
2013-10-09 11:43:19 +02:00
|
|
|
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
2010-09-30 09:34:38 -04:00
|
|
|
dst += 16;
|
|
|
|
}
|
2013-09-05 08:53:36 +02:00
|
|
|
tnz >>= 2;
|
|
|
|
lnz = (lnz >> 1) | (l << 5);
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
2013-09-05 08:53:36 +02:00
|
|
|
// Note: we don't really need the per-4x4 details for U/V blocks.
|
2013-10-08 22:05:38 +02:00
|
|
|
non_zero_uv |= nz_coeffs << (4 * ch);
|
2013-09-05 08:53:36 +02:00
|
|
|
out_t_nz |= (tnz << 4) << ch;
|
|
|
|
out_l_nz |= (lnz & 0xf0) << ch;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
mb->nz_ = out_t_nz;
|
|
|
|
left_mb->nz_ = out_l_nz;
|
|
|
|
|
2013-10-08 22:05:38 +02:00
|
|
|
block->non_zero_y_ = non_zero_y;
|
|
|
|
block->non_zero_uv_ = non_zero_uv;
|
2013-11-26 22:59:02 +01:00
|
|
|
|
|
|
|
// We look at the mode-code of each block and check if some blocks have less
|
|
|
|
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
|
|
|
|
// empty blocks.
|
|
|
|
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
|
|
|
|
|
2013-10-08 22:05:38 +02:00
|
|
|
return !(non_zero_y | non_zero_uv); // will be used for further optimization
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2010-09-30 09:34:38 -04:00
|
|
|
// Main loop
|
|
|
|
|
2011-03-09 21:29:36 -08:00
|
|
|
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
|
|
|
VP8MB* const left = dec->mb_info_ - 1;
|
2013-05-15 20:03:15 +02:00
|
|
|
VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
|
2013-10-10 21:29:58 +02:00
|
|
|
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
2014-01-28 21:40:40 +01:00
|
|
|
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
|
2011-03-09 21:29:36 -08:00
|
|
|
|
2013-05-15 20:03:15 +02:00
|
|
|
if (!skip) {
|
|
|
|
skip = ParseResiduals(dec, mb, token_br);
|
2011-03-09 21:29:36 -08:00
|
|
|
} else {
|
2013-05-15 20:03:15 +02:00
|
|
|
left->nz_ = mb->nz_ = 0;
|
2013-05-31 22:38:12 +02:00
|
|
|
if (!block->is_i4x4_) {
|
2013-05-15 20:03:15 +02:00
|
|
|
left->nz_dc_ = mb->nz_dc_ = 0;
|
2011-03-09 21:29:36 -08:00
|
|
|
}
|
2013-10-08 22:05:38 +02:00
|
|
|
block->non_zero_y_ = 0;
|
|
|
|
block->non_zero_uv_ = 0;
|
2015-02-02 19:28:34 -08:00
|
|
|
block->dither_ = 0;
|
2011-03-09 21:29:36 -08:00
|
|
|
}
|
|
|
|
|
2012-12-14 10:22:54 -08:00
|
|
|
if (dec->filter_type_ > 0) { // store filter info
|
|
|
|
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
|
2014-01-28 21:40:40 +01:00
|
|
|
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
|
2013-10-28 01:46:11 -07:00
|
|
|
finfo->f_inner_ |= !skip;
|
2012-12-14 10:22:54 -08:00
|
|
|
}
|
|
|
|
|
2013-05-15 20:03:15 +02:00
|
|
|
return !token_br->eof_;
|
2011-03-09 21:29:36 -08:00
|
|
|
}
|
2010-09-30 09:34:38 -04:00
|
|
|
|
2011-07-22 13:09:10 -07:00
|
|
|
void VP8InitScanline(VP8Decoder* const dec) {
|
|
|
|
VP8MB* const left = dec->mb_info_ - 1;
|
|
|
|
left->nz_ = 0;
|
2013-05-15 20:03:15 +02:00
|
|
|
left->nz_dc_ = 0;
|
2011-07-22 13:09:10 -07:00
|
|
|
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
2013-10-10 21:29:58 +02:00
|
|
|
dec->mb_x_ = 0;
|
2011-07-22 13:09:10 -07:00
|
|
|
}
|
|
|
|
|
2011-03-09 21:29:36 -08:00
|
|
|
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
2011-06-20 00:45:15 -07:00
|
|
|
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
2013-10-10 21:29:58 +02:00
|
|
|
// Parse bitstream for this row.
|
2011-03-09 21:29:36 -08:00
|
|
|
VP8BitReader* const token_br =
|
2016-04-30 00:16:59 -07:00
|
|
|
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
|
2014-01-28 21:40:40 +01:00
|
|
|
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"Premature end-of-partition0 encountered.");
|
|
|
|
}
|
2013-10-10 21:29:58 +02:00
|
|
|
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
2011-03-09 21:29:36 -08:00
|
|
|
if (!VP8DecodeMB(dec, token_br)) {
|
|
|
|
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
|
|
|
"Premature end-of-file encountered.");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
}
|
2013-10-10 21:29:58 +02:00
|
|
|
VP8InitScanline(dec); // Prepare for next scanline
|
|
|
|
|
2013-10-15 00:25:21 +02:00
|
|
|
// Reconstruct, filter and emit the row.
|
2011-07-22 13:09:10 -07:00
|
|
|
if (!VP8ProcessRow(dec, io)) {
|
2011-06-20 00:45:15 -07:00
|
|
|
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
}
|
2013-10-15 23:58:31 +02:00
|
|
|
if (dec->mt_method_ > 0) {
|
2014-06-12 11:35:44 +02:00
|
|
|
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
|
2011-07-22 13:09:10 -07:00
|
|
|
}
|
2010-09-30 09:34:38 -04:00
|
|
|
|
2011-02-16 14:33:16 -08:00
|
|
|
return 1;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Main entry point
|
|
|
|
int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
2011-07-22 13:09:10 -07:00
|
|
|
int ok = 0;
|
2010-09-30 09:34:38 -04:00
|
|
|
if (dec == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (io == NULL) {
|
2011-02-16 14:33:16 -08:00
|
|
|
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
|
|
|
"NULL VP8Io parameter in VP8Decode().");
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dec->ready_) {
|
|
|
|
if (!VP8GetHeaders(dec, io)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(dec->ready_);
|
|
|
|
|
2011-07-22 13:09:10 -07: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 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
2011-07-22 13:09:10 -07:00
|
|
|
if (!ok) {
|
2010-09-30 09:34:38 -04:00
|
|
|
VP8Clear(dec);
|
2011-06-20 00:45:15 -07:00
|
|
|
return 0;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
dec->ready_ = 0;
|
2012-04-10 18:41:18 -07:00
|
|
|
return ok;
|
2010-09-30 09:34:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void VP8Clear(VP8Decoder* const dec) {
|
|
|
|
if (dec == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
2014-06-12 11:35:44 +02:00
|
|
|
WebPGetWorkerInterface()->End(&dec->worker_);
|
2016-04-04 15:50:57 +02:00
|
|
|
WebPDeallocateAlphaMemory(dec);
|
2014-03-27 23:27:32 +01:00
|
|
|
WebPSafeFree(dec->mem_);
|
2010-09-30 09:34:38 -04:00
|
|
|
dec->mem_ = NULL;
|
|
|
|
dec->mem_size_ = 0;
|
|
|
|
memset(&dec->br_, 0, sizeof(dec->br_));
|
|
|
|
dec->ready_ = 0;
|
|
|
|
}
|
2011-03-09 21:25:53 -08:00
|
|
|
|
2011-08-25 14:22:32 -07:00
|
|
|
//------------------------------------------------------------------------------
|