in Extended/libwebp/src/dec/vp8l_dec.c [1110:1255]
static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
int width, int height, int last_row,
ProcessRowsFunc process_func) {
int row = dec->last_pixel_ / width;
int col = dec->last_pixel_ % width;
VP8LBitReader* const br = &dec->br_;
VP8LMetadata* const hdr = &dec->hdr_;
uint32_t* src = data + dec->last_pixel_;
uint32_t* last_cached = src;
uint32_t* const src_end = data + width * height; // End of 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_;
const HTreeGroup* htree_group =
(src < src_last) ? GetHtreeGroupForPos(hdr, col, row) : NULL;
assert(dec->last_row_ < last_row);
assert(src_last <= src_end);
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.
if ((col & mask) == 0) {
htree_group = GetHtreeGroupForPos(hdr, col, row);
}
assert(htree_group != NULL);
if (htree_group->is_trivial_code) {
*src = htree_group->literal_arb;
goto AdvanceByOne;
}
VP8LFillBitWindow(br);
if (htree_group->use_packed_table) {
code = ReadPackedSymbols(htree_group, br, src);
if (VP8LIsEndOfStream(br)) break;
if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne;
} else {
code = ReadSymbol(htree_group->htrees[GREEN], br);
}
if (VP8LIsEndOfStream(br)) break;
if (code < NUM_LITERAL_CODES) { // Literal
if (htree_group->is_trivial_literal) {
*src = htree_group->literal_arb | (code << 8);
} else {
int red, blue, alpha;
red = ReadSymbol(htree_group->htrees[RED], br);
VP8LFillBitWindow(br);
blue = ReadSymbol(htree_group->htrees[BLUE], br);
alpha = ReadSymbol(htree_group->htrees[ALPHA], br);
if (VP8LIsEndOfStream(br)) break;
*src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue;
}
AdvanceByOne:
++src;
++col;
if (col >= width) {
col = 0;
++row;
if (process_func != NULL) {
if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
process_func(dec, row);
}
}
if (color_cache != NULL) {
while (last_cached < src) {
VP8LColorCacheInsert(color_cache, *last_cached++);
}
}
}
} else if (code < len_code_limit) { // Backward reference
int dist_code, dist;
const int length_sym = code - NUM_LITERAL_CODES;
const int length = GetCopyLength(length_sym, br);
const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br);
VP8LFillBitWindow(br);
dist_code = GetCopyDistance(dist_symbol, br);
dist = PlaneCodeToDistance(width, dist_code);
if (VP8LIsEndOfStream(br)) break;
if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) {
goto Error;
} else {
CopyBlock32b(src, dist, length);
}
src += length;
col += length;
while (col >= width) {
col -= width;
++row;
if (process_func != NULL) {
if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
process_func(dec, row);
}
}
}
// Because of the check done above (before 'src' was incremented by
// 'length'), the following holds true.
assert(src <= src_end);
if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row);
if (color_cache != NULL) {
while (last_cached < src) {
VP8LColorCacheInsert(color_cache, *last_cached++);
}
}
} else if (code < color_cache_limit) { // Color cache
const int key = code - len_code_limit;
assert(color_cache != NULL);
while (last_cached < src) {
VP8LColorCacheInsert(color_cache, *last_cached++);
}
*src = VP8LColorCacheLookup(color_cache, key);
goto AdvanceByOne;
} else { // Not reached
goto Error;
}
}
br->eos_ = VP8LIsEndOfStream(br);
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 > last_row ? last_row : row);
}
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;
}