add lossless incremental decoding support

* We don't need to change DecodeAlpha, since incremental
decoding is not useful for Alpha (we already decode
progressively along the RGB)
* Similarly, we don't do incremental decoding for level>0 planes:
   the metadata don't turn into visible pixel (only the ones in level0), so...
(No visible speed change)

Change-Id: I2fd4b9ba227561a7dbede647686584b752be7baa
This commit is contained in:
Pascal Massimino 2014-09-24 09:38:52 +02:00
parent d4471637ef
commit f060dfc422
5 changed files with 96 additions and 39 deletions

View File

@ -502,7 +502,12 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
dec->status_ = VP8_STATUS_SUSPENDED;
return ErrorStatusLossless(idec, dec->status_);
}
if (!VP8LDecodeHeader(dec, io)) {
if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
curr_size < idec->chunk_size_) {
dec->status_ = VP8_STATUS_SUSPENDED;
}
return ErrorStatusLossless(idec, dec->status_);
}
// Allocate/verify output buffer now.
@ -521,23 +526,15 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
const size_t curr_size = MemDataSize(&idec->mem_);
assert(idec->is_lossless_);
// At present Lossless decoder can't decode image incrementally. So wait till
// all the image data is aggregated before image can be decoded.
if (curr_size < idec->chunk_size_) {
return VP8_STATUS_SUSPENDED;
}
// Switch to incremental decoding if we don't have all the bytes available.
dec->incremental_ = (curr_size < idec->chunk_size_);
if (!VP8LDecodeImage(dec)) {
// The decoding is called after all the data-bytes are aggregated. Change
// the error to VP8_BITSTREAM_ERROR in case lossless decoder fails to decode
// all the pixels (VP8_STATUS_SUSPENDED).
if (dec->status_ == VP8_STATUS_SUSPENDED) {
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
}
return ErrorStatusLossless(idec, dec->status_);
}
return FinishDecoding(idec);
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
: FinishDecoding(idec);
}
// Main decoding loop
@ -853,4 +850,3 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
return 1;
}

View File

@ -258,6 +258,7 @@ static int ReadHuffmanCodeLengths(
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
int* const code_lengths, HuffmanCode* const table) {
int ok = 0;
int size = 0;
VP8LBitReader* const br = &dec->br_;
const int simple_code = VP8LReadBits(br, 1);
@ -292,12 +293,15 @@ static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
}
ok = ok && !br->eos_;
if (!ok) {
if (ok) {
size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS,
code_lengths, alphabet_size);
}
if (!ok || size == 0) {
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
return 0;
}
return VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS,
code_lengths, alphabet_size);
return size;
}
static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
@ -962,10 +966,29 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data,
return ok;
}
static void SaveState(VP8LDecoder* const dec, int last_pixel) {
assert(dec->incremental_);
dec->saved_br_ = dec->br_;
dec->saved_last_pixel_ = last_pixel;
if (dec->hdr_.color_cache_size_ > 0) {
VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_);
}
}
static void RestoreState(VP8LDecoder* const dec) {
assert(dec->br_.eos_);
dec->status_ = VP8_STATUS_SUSPENDED;
dec->br_ = dec->saved_br_;
dec->last_pixel_ = dec->saved_last_pixel_;
if (dec->hdr_.color_cache_size_ > 0) {
VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_);
}
}
#define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points
static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
int width, int height, int last_row,
ProcessRowsFunc process_func) {
int ok = 1;
int row = dec->last_pixel_ / width;
int col = dec->last_pixel_ % width;
VP8LBitReader* const br = &dec->br_;
@ -977,6 +1000,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
uint32_t* const src_last = data + width * last_row; // Last pixel to decode
const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
const int color_cache_limit = len_code_limit + hdr->color_cache_size_;
int next_sync_row = dec->incremental_ ? row : 1 << 24;
VP8LColorCache* const color_cache =
(hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL;
const int mask = hdr->huffman_mask_;
@ -984,8 +1008,12 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
assert(src < src_end);
assert(src_last <= src_end);
while (!br->eos_ && src < src_last) {
while (src < src_last) {
int code;
if (row >= next_sync_row) {
SaveState(dec, (int)(src - data));
next_sync_row = row + SYNC_EVERY_N_ROWS;
}
// Only update when changing tile. Note we could use this test:
// if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed
// but that's actually slower and needs storing the previous col/row.
@ -994,6 +1022,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
}
VP8LFillBitWindow(br);
code = ReadSymbol(htree_group->htrees[GREEN], br);
if (br->eos_) break; // early out
if (code < NUM_LITERAL_CODES) { // Literal
if (htree_group->is_trivial_literal) {
*src = htree_group->literal_arb | (code << 8);
@ -1003,6 +1032,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
VP8LFillBitWindow(br);
blue = ReadSymbol(htree_group->htrees[BLUE], br);
alpha = ReadSymbol(htree_group->htrees[ALPHA], br);
if (br->eos_) break;
*src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue;
}
AdvanceByOne:
@ -1028,9 +1058,9 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
VP8LFillBitWindow(br);
dist_code = GetCopyDistance(dist_symbol, br);
dist = PlaneCodeToDistance(width, dist_code);
if (br->eos_) break;
if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) {
ok = 0;
goto End;
goto Error;
} else {
CopyBlock32b(src, dist, length);
}
@ -1060,23 +1090,30 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
*src = VP8LColorCacheLookup(color_cache, key);
goto AdvanceByOne;
} else { // Not reached
ok = 0;
goto End;
goto Error;
}
assert(br->eos_ == VP8LIsEndOfStream(br));
}
// Process the remaining rows corresponding to last row-block.
if (process_func != NULL) process_func(dec, row);
End:
if (!ok || (br->eos_ && src < src_end)) {
ok = 0;
dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED
: VP8_STATUS_BITSTREAM_ERROR;
if (dec->incremental_ && br->eos_ && src < src_end) {
RestoreState(dec);
} else if (!br->eos_) {
// Process the remaining rows corresponding to last row-block.
if (process_func != NULL) {
process_func(dec, row);
}
dec->status_ = VP8_STATUS_OK;
dec->last_pixel_ = (int)(src - data); // end-of-scan marker
} else {
dec->last_pixel_ = (int)(src - data);
// if not incremental, and we are past the end of buffer (eos_=1), then this
// is a real bitstream error.
goto Error;
}
return ok;
return 1;
Error:
dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
return 0;
}
// -----------------------------------------------------------------------------
@ -1181,6 +1218,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) {
WebPSafeFree(hdr->huffman_tables_);
VP8LHtreeGroupsFree(hdr->htree_groups_);
VP8LColorCacheClear(&hdr->color_cache_);
VP8LColorCacheClear(&hdr->saved_color_cache_);
InitMetadata(hdr);
}
@ -1308,12 +1346,6 @@ static int DecodeImageStream(int xsize, int ysize,
if (!ok) {
WebPSafeFree(data);
ClearMetadata(hdr);
// Check status coherency.
if (dec->br_.eos_) {
assert(dec->status_ == VP8_STATUS_SUSPENDED);
} else {
assert(dec->status_ == VP8_STATUS_BITSTREAM_ERROR);
}
} else {
if (decoded_data != NULL) {
*decoded_data = data;
@ -1520,6 +1552,16 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
// need the alpha-multiply functions for premultiplied output or rescaling
WebPInitAlphaProcessing();
}
if (dec->incremental_) {
if (dec->hdr_.color_cache_size_ > 0 &&
dec->hdr_.saved_color_cache_.colors_ == NULL) {
if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_,
dec->hdr_.color_cache_.hash_bits_)) {
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
goto Err;
}
}
}
dec->state_ = READ_DATA;
}

View File

@ -43,6 +43,7 @@ struct VP8LTransform {
typedef struct {
int color_cache_size_;
VP8LColorCache color_cache_;
VP8LColorCache saved_color_cache_; // for incremental
int huffman_mask_;
int huffman_subsample_bits_;
@ -66,6 +67,9 @@ struct VP8LDecoder {
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
VP8LBitReader br_;
int incremental_; // if true, incremental decoding is expected
VP8LBitReader saved_br_; // note: could be local variables too
int saved_last_pixel_;
int width_;
int height_;
@ -74,6 +78,7 @@ struct VP8LDecoder {
// not be transformed, scaled and
// color-converted yet.
int last_out_row_; // last row output so far.
int last_cached_;
VP8LMetadata hdr_;

View File

@ -13,6 +13,7 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "./color_cache.h"
#include "../utils/utils.h"
@ -27,6 +28,7 @@ int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
sizeof(*cc->colors_));
if (cc->colors_ == NULL) return 0;
cc->hash_shift_ = 32 - hash_bits;
cc->hash_bits_ = hash_bits;
return 1;
}
@ -37,3 +39,11 @@ void VP8LColorCacheClear(VP8LColorCache* const cc) {
}
}
void VP8LColorCacheCopy(const VP8LColorCache* const src,
VP8LColorCache* const dst) {
assert(src != NULL);
assert(dst != NULL);
assert(src->hash_bits_ == dst->hash_bits_);
memcpy(dst->colors_, src->colors_,
(1 << dst->hash_bits_) * sizeof(*dst->colors_));
}

View File

@ -24,7 +24,8 @@ extern "C" {
// Main color cache struct.
typedef struct {
uint32_t *colors_; // color entries
int hash_shift_; // Hash shift: 32 - hash_bits.
int hash_shift_; // Hash shift: 32 - hash_bits_.
int hash_bits_;
} VP8LColorCache;
static const uint32_t kHashMul = 0x1e35a7bd;
@ -58,6 +59,9 @@ static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
// Returns false in case of memory error.
int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits);
void VP8LColorCacheCopy(const VP8LColorCache* const src,
VP8LColorCache* const dst);
// Delete the memory associated to color cache.
void VP8LColorCacheClear(VP8LColorCache* const color_cache);