mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
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:
parent
d4471637ef
commit
f060dfc422
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
} else {
|
||||
dec->last_pixel_ = (int)(src - data);
|
||||
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);
|
||||
}
|
||||
return ok;
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
dec->last_pixel_ = (int)(src - data); // end-of-scan marker
|
||||
} else {
|
||||
// if not incremental, and we are past the end of buffer (eos_=1), then this
|
||||
// is a real bitstream error.
|
||||
goto Error;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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_));
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user