webp/src/vp8.c
Pascal Massimino 6a37a2aaa9 fancy chroma upscaling
When FANCY_UPSCALING is defined, use a smoothing filter for upscaling
the U/V chroma fields. The filter used is a separable t[1 3 3 1] x [1 3 3 1]
filter. It can be easily changed in macros MIX_*.

The upscaling code reside on the thing shell between user and core
decoding (in webp.c), and not in the core decoder. As such, this smoothing
process can still be offloaded to GPU in some future and is not integral
part of the decoding process.

Coincidentaly: changed the way data is tranfered to user. For profile 2 (no
filtering), it used to be on a per-block basis. Now, for all profiles, we
emit rows of pixels (between 8 and 24 in height) when they are ready.
This makes the upscaling code much easier.

Will update the test vectors MD5 sums soon (as they'll be broken
after this change)

Change-Id: I2640ff12596cb8b843a4a376d7347447d9b9f778
2010-11-03 15:05:48 -07:00

615 lines
17 KiB
C

// Copyright 2010 Google Inc.
//
// 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>
#include "vp8i.h"
//-----------------------------------------------------------------------------
// VP8Decoder
static void SetOk(VP8Decoder* const dec) {
dec->status_ = 0;
dec->error_msg_ = "OK";
}
void VP8InitIo(VP8Io* const io) {
if (io) {
memset(io, 0, sizeof(*io));
}
}
VP8Decoder* VP8New() {
VP8Decoder* dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder));
if (dec) {
SetOk(dec);
dec->ready_ = 0;
}
return dec;
}
int VP8Status(VP8Decoder* const dec) {
if (!dec) return 2;
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);
}
}
int VP8SetError(VP8Decoder* const dec, int error, const char *msg) {
dec->status_ = error;
dec->error_msg_ = msg;
dec->ready_ = 0;
return 0;
}
//-----------------------------------------------------------------------------
// 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);
if (VP8Get(br)) { // update data
int s;
hdr->absolute_delta_ = VP8Get(br);
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
}
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
}
}
if (hdr->update_map_) {
int s;
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
}
}
} else {
hdr->update_map_ = 0;
}
return 1;
}
// Paragraph 9.5
static int ParsePartitions(VP8Decoder* const dec,
const uint8_t* buf, uint32_t size) {
VP8BitReader* const br = &dec->br_;
const uint8_t* sz = buf;
int last_part;
uint32_t offset;
int p;
dec->num_parts_ = 1 << VP8GetValue(br, 2);
last_part = dec->num_parts_ - 1;
offset = last_part * 3;
if (size <= offset) {
return 0;
}
for (p = 0; p < last_part; ++p) {
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
if (offset + psize > size) {
return 0;
}
VP8Init(dec->parts_ + p, buf + offset, psize);
offset += psize;
sz += 3;
}
size -= offset;
VP8Init(dec->parts_ + last_part, buf + offset, size);
return 1;
}
// 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?
int i;
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
if (VP8Get(br)) {
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
}
}
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
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_) {
int s;
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
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_;
}
}
return 1;
}
static inline uint32_t get_le32(const uint8_t* const data) {
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
// Topmost call
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
uint8_t* buf;
uint32_t buf_size;
VP8FrameHeader* frm_hdr;
VP8PictureHeader* pic_hdr;
VP8BitReader* br;
if (dec == NULL) {
return 0;
}
SetOk(dec);
if (io == NULL || io->data == NULL || io->data_size <= 4) {
return VP8SetError(dec, 2, "null VP8Io passed to VP8GetHeaders()");
}
buf = (uint8_t *)io->data;
buf_size = io->data_size;
if (buf_size < 4) {
return VP8SetError(dec, 2, "Not enough data to parse frame header");
}
// Skip over valid RIFF headers
if (!memcmp(buf, "RIFF", 4)) {
uint32_t riff_size;
uint32_t chunk_size;
if (buf_size < 20 + 4) {
return VP8SetError(dec, 2, "RIFF: Truncated header.");
}
if (memcmp(buf + 8, "WEBP", 4)) { // wrong image file signature
return VP8SetError(dec, 2, "RIFF: WEBP signature not found.");
}
riff_size = get_le32(buf + 4);
if (memcmp(buf + 12, "VP8 ", 4)) {
return VP8SetError(dec, 2, "RIFF: Invalid compression format.");
}
chunk_size = get_le32(buf + 16);
if ((chunk_size > riff_size + 8) || (chunk_size & 1)) {
return VP8SetError(dec, 2, "RIFF: Inconsistent size information.");
}
buf += 20;
buf_size -= 20;
}
// Paragraph 9.1
{
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
frm_hdr = &dec->frm_hdr_;
frm_hdr->key_frame_ = !(bits & 1);
frm_hdr->profile_ = (bits >> 1) & 7;
frm_hdr->show_ = (bits >> 4) & 1;
frm_hdr->partition_length_ = (bits >> 5);
buf += 3;
buf_size -= 3;
}
pic_hdr = &dec->pic_hdr_;
if (frm_hdr->key_frame_) {
// Paragraph 9.2
if (buf_size < 7) {
return VP8SetError(dec, 2, "cannot parse picture header");
}
if (buf[0] != 0x9d || buf[1] != 0x01 || buf[2] != 0x2a) {
return VP8SetError(dec, 2, "Bad code word");
}
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;
io->width = pic_hdr->width_;
io->height = pic_hdr->height_;
VP8ResetProba(&dec->proba_);
ResetSegmentHeader(&dec->segment_hdr_);
dec->segment_ = 0; // default for intra
}
br = &dec->br_;
VP8Init(br, buf, buf_size);
buf += frm_hdr->partition_length_;
buf_size -= frm_hdr->partition_length_;
if (frm_hdr->key_frame_) {
pic_hdr->colorspace_ = VP8Get(br);
pic_hdr->clamp_type_ = VP8Get(br);
}
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
return VP8SetError(dec, 2, "cannot parse segment header");
}
// Filter specs
if (!ParseFilterHeader(br, dec)) {
return VP8SetError(dec, 2, "cannot parse filter header");
}
if (!ParsePartitions(dec, buf, buf_size)) {
return VP8SetError(dec, 2, "cannot parse partitions");
}
// 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
return VP8SetError(dec, 2, "Not a key frame.");
#endif
} else {
dec->buffer_flags_ = 0x003 | 0x100;
}
// Paragraph 9.8
dec->update_proba_ = VP8Get(br);
if (!dec->update_proba_) { // save for later restore
dec->proba_saved_ = dec->proba_;
}
#ifndef ONLY_KEYFRAME_CODE
dec->buffer_flags_ &= 1 << 8;
dec->buffer_flags_ |=
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
#endif
VP8ParseProba(br, dec);
// sanitized state
dec->ready_ = 1;
return 1;
}
//-----------------------------------------------------------------------------
// 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
};
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};
static const uint8_t kCat6[] =
{254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0};
static const uint8_t * const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
static const uint8_t kZigzag[16] = {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
};
typedef const uint8_t PROBA_ARRAY[NUM_CTX][NUM_PROBAS];
static int GetCoeffs(VP8BitReader* const br,
const uint8_t (*prob)[NUM_CTX][NUM_PROBAS],
int ctx, const uint16_t dq[2], int n, int16_t* out) {
const uint8_t* p = prob[kBands[n]][ctx];
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
return -1;
}
while (1) {
++n;
if (!VP8GetBit(br, p[1])) {
p = prob[kBands[n]][0];
} else { // non zero coeff
int v, j;
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 {
v = 7 + 2 * VP8GetBit(br, 165) + VP8GetBit(br, 145);
}
} else {
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 = (uint8_t*)kCat3456[cat]; *tab; ++tab) {
v += v + VP8GetBit(br, *tab);
}
v += 3 + (8 << cat);
}
}
p = prob[kBands[n]][2];
}
j = kZigzag[n - 1];
out[j] = VP8GetSigned(br, v) * dq[j > 0];
if (n == 16) break;
if (!VP8GetBit(br, p[0])) { // EOB
return n;
}
}
}
return 15;
}
// Table to unpack four bits into four bytes
static const uint8_t kUnpackTab[16][4] = {
{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} };
// Macro to pack four LSB of four bytes into four bits.
#define PACK(X, S) \
((((*(uint32_t*)(X)) * 0x01020408U) & 0xff000000) >> (S))
typedef const uint8_t (*Proba_t)[NUM_CTX][NUM_PROBAS]; // for const-casting
static int ParseResiduals(VP8Decoder* const dec,
VP8MB* const mb, VP8BitReader* const token_br) {
int out_t_nz, out_l_nz, first;
Proba_t ac_prob;
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
int16_t* dst = dec->coeffs_;
VP8MB* const left_mb = dec->mb_info_ - 1;
uint8_t nz_ac[4], nz_dc[4];
uint32_t non_zero_ac = 0;
uint32_t non_zero_dc = 0;
uint8_t tnz[4], lnz[4];
int x, y, ch;
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_;
const int last = GetCoeffs(token_br, (Proba_t)dec->proba_.coeffs_[1],
ctx, q->y2_mat_, 0, dc);
mb->dc_nz_ = left_mb->dc_nz_ = (last >= 0);
first = 1;
ac_prob = (Proba_t)dec->proba_.coeffs_[0];
VP8TransformWHT(dc, dst);
} else {
first = 0;
ac_prob = (Proba_t)dec->proba_.coeffs_[3];
}
memcpy(tnz, kUnpackTab[mb->nz_ & 0xf], sizeof(tnz));
memcpy(lnz, kUnpackTab[left_mb->nz_ & 0xf], sizeof(lnz));
for (y = 0; y < 4; ++y) {
int l = lnz[y];
for (x = 0; x < 4; ++x) {
const int ctx = l + tnz[x];
const int last = GetCoeffs(token_br, ac_prob, ctx,
q->y1_mat_, first, dst);
nz_dc[x] = (dst[0] != 0);
nz_ac[x] = (last > 0);
tnz[x] = l = (last >= 0);
dst += 16;
}
lnz[y] = l;
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);
memcpy(tnz, kUnpackTab[mb->nz_ >> 4], sizeof(tnz));
memcpy(lnz, kUnpackTab[left_mb->nz_ >> 4], sizeof(lnz));
for (ch = 0; ch < 4; ch += 2) {
for (y = 0; y < 2; ++y) {
int l = lnz[ch + y];
for (x = 0; x < 2; ++x) {
const int ctx = l + tnz[ch + x];
const int last =
GetCoeffs(token_br, (Proba_t)dec->proba_.coeffs_[2],
ctx, q->uv_mat_, 0, dst);
nz_dc[y * 2 + x] = (dst[0] != 0);
nz_ac[y * 2 + x] = (last > 0);
tnz[ch + x] = l = (last >= 0);
dst += 16;
}
lnz[ch + y] = l;
}
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_;
return 1;
}
#undef PACK
//-----------------------------------------------------------------------------
// Main loop
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
int ok = 1;
VP8BitReader* const br = &dec->br_;
VP8BitReader* token_br;
for (dec->mb_y_ = 0; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
VP8MB* const left = dec->mb_info_ - 1;
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
left->nz_ = 0;
left->dc_nz_ = 0;
token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
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 (!info->skip_) {
if (!ParseResiduals(dec, info, token_br)) {
ok = 0;
break;
}
} 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;
}
VP8ReconstructBlock(dec);
// Store data and save block's filtering params
VP8StoreBlock(dec, io);
}
if (!ok) {
break;
}
VP8FinishRow(dec, io);
if (dec->br_.eof_ || token_br->eof_) {
ok = 0;
break;
}
}
// Finish
if (!dec->update_proba_) {
dec->proba_ = dec->proba_saved_;
}
return ok;
}
// Main entry point
int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
if (dec == NULL) {
return 0;
}
if (io == NULL) {
return VP8SetError(dec, 2, "NULL VP8Io parameter in VP8Decode().");
}
if (!dec->ready_) {
if (!VP8GetHeaders(dec, io)) {
return 0;
}
}
assert(dec->ready_);
// will allocate memory and prepare everything.
if (!VP8InitFrame(dec, io)) {
VP8Clear(dec);
return VP8SetError(dec, 3, "Allocation failed");
}
if (io->setup && !io->setup(io)) {
VP8Clear(dec);
return VP8SetError(dec, 3, "Frame setup failed");
}
// Main decoding loop
{
const int ret = ParseFrame(dec, io);
if (io->teardown) {
io->teardown(io);
}
if (!ret) {
VP8Clear(dec);
return VP8SetError(dec, 3, "Frame decoding failed");
}
}
dec->ready_ = 0;
return 1;
}
void VP8Clear(VP8Decoder* const dec) {
if (dec == NULL) {
return;
}
if (dec->mem_) {
free(dec->mem_);
}
dec->mem_ = NULL;
dec->mem_size_ = 0;
memset(&dec->br_, 0, sizeof(dec->br_));
dec->ready_ = 0;
}