diff options
Diffstat (limited to 'third_party/libwebp/dec/vp8.c')
-rw-r--r-- | third_party/libwebp/dec/vp8.c | 236 |
1 files changed, 158 insertions, 78 deletions
diff --git a/third_party/libwebp/dec/vp8.c b/third_party/libwebp/dec/vp8.c index 43a0c35..9149284 100644 --- a/third_party/libwebp/dec/vp8.c +++ b/third_party/libwebp/dec/vp8.c @@ -11,18 +11,19 @@ #include <stdlib.h> #include "vp8i.h" +#include "webpi.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ int WebPGetDecoderVersion(void) { return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // VP8Decoder static void SetOk(VP8Decoder* const dec) { @@ -43,6 +44,7 @@ VP8Decoder* VP8New(void) { VP8Decoder* dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder)); if (dec) { SetOk(dec); + WebPWorkerInit(&dec->worker_); dec->ready_ = 0; } return dec; @@ -74,7 +76,56 @@ int VP8SetError(VP8Decoder* const dec, return 0; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ + +int VP8GetInfo(const uint8_t* data, uint32_t data_size, uint32_t chunk_size, + int* width, int* height, int* has_alpha) { + if (data_size < 10) { + return 0; // not enough data + } + // check signature + if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) { + 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 (has_alpha) { +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (data_size < 11) return 0; + *has_alpha = !!(data[10] & 0x80); // the colorspace_ bit +#else + *has_alpha = 0; +#endif + } + 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. + } + + if (width) { + *width = w; + } + if (height) { + *height = h; + } + + return 1; + } +} + +//------------------------------------------------------------------------------ // Header parsing static void ResetSegmentHeader(VP8SegmentHeader* const hdr) { @@ -194,14 +245,12 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) { return !br->eof_; } -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; + const uint8_t* buf; uint32_t buf_size; + uint32_t vp8_chunk_size; + uint32_t bytes_skipped; VP8FrameHeader* frm_hdr; VP8PictureHeader* pic_hdr; VP8BitReader* br; @@ -216,41 +265,19 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { "null VP8Io passed to VP8GetHeaders()"); } - buf = (uint8_t *)io->data; + buf = io->data; buf_size = io->data_size; - if (buf == NULL || buf_size <= 4) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "Not enough data to parse frame header"); + + // Process Pre-VP8 chunks. + status = WebPParseHeaders(&buf, &buf_size, &vp8_chunk_size, &bytes_skipped); + if (status != VP8_STATUS_OK) { + return VP8SetError(dec, status, "Incorrect/incomplete 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, VP8_STATUS_NOT_ENOUGH_DATA, - "RIFF: Truncated header."); - } - if (memcmp(buf + 8, "WEBP", 4)) { // wrong image file signature - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "RIFF: WEBP signature not found."); - } - riff_size = get_le32(buf + 4); - if (riff_size < 12) { - return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, - "RIFF: Truncated header."); - } - if (memcmp(buf + 12, "VP8 ", 4)) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "RIFF: Invalid compression format."); - } - chunk_size = get_le32(buf + 16); - if (chunk_size > riff_size - 12) { - return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, - "RIFF: Inconsistent size information."); - } - buf += 20; - buf_size -= 20; + // Process the VP8 frame header. + if (buf_size < 4) { + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Truncated header."); } // Paragraph 9.1 @@ -291,8 +318,17 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { dec->mb_w_ = (pic_hdr->width_ + 15) >> 4; dec->mb_h_ = (pic_hdr->height_ + 15) >> 4; + // Setup default output area (can be later modified during io->setup()) io->width = pic_hdr->width_; io->height = pic_hdr->height_; + io->use_scaling = 0; + io->use_cropping = 0; + io->crop_top = 0; + io->crop_left = 0; + io->crop_right = io->width; + io->crop_bottom = io->height; + io->mb_w = io->width; // sanity check + io->mb_h = io->height; // ditto VP8ResetProba(&dec->proba_); ResetSegmentHeader(&dec->segment_hdr_); @@ -305,6 +341,10 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "bad partition length"); } + + dec->alpha_data_ = NULL; + dec->alpha_data_size_ = 0; + br = &dec->br_; VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); buf += frm_hdr->partition_length_; @@ -368,12 +408,42 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { VP8ParseProba(br, dec); +#ifdef WEBP_EXPERIMENTAL_FEATURES + // Extensions + if (dec->pic_hdr_.colorspace_) { + const size_t kTrailerSize = 8; + const uint8_t kTrailerMarker = 0x01; + const uint8_t* ext_buf = buf - kTrailerSize; + size_t size; + + if (frm_hdr->partition_length_ < kTrailerSize || + ext_buf[kTrailerSize - 1] != kTrailerMarker) { + Error: + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "RIFF: Inconsistent extra information."); + } + // Alpha + size = (ext_buf[4] << 0) | (ext_buf[5] << 8) | (ext_buf[6] << 16); + if (frm_hdr->partition_length_ < size + kTrailerSize) { + goto Error; + } + dec->alpha_data_ = (size > 0) ? ext_buf - size : NULL; + dec->alpha_data_size_ = size; + + // Layer + size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16); + dec->layer_data_size_ = size; + dec->layer_data_ = NULL; // will be set later + dec->layer_colorspace_ = ext_buf[3]; + } +#endif + // sanitized state dec->ready_ = 1; return 1; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Residual decoding (Paragraph 13.2 / 13.3) static const uint8_t kBands[16 + 1] = { @@ -386,7 +456,7 @@ 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* 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 }; @@ -422,7 +492,8 @@ static int GetCoeffs(VP8BitReader* const br, ProbaArray prob, if (!VP8GetBit(br, p[7])) { v = 5 + VP8GetBit(br, 159); } else { - v = 7 + 2 * VP8GetBit(br, 165) + VP8GetBit(br, 145); + v = 7 + 2 * VP8GetBit(br, 165); + v += VP8GetBit(br, 145); } } else { const uint8_t* tab; @@ -551,7 +622,7 @@ static void ParseResiduals(VP8Decoder* const dec, } #undef PACK -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // Main loop int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { @@ -588,16 +659,21 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) { return (!token_br->eof_); } +void VP8InitScanline(VP8Decoder* const dec) { + VP8MB* const left = dec->mb_info_ - 1; + left->nz_ = 0; + left->dc_nz_ = 0; + memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); + dec->filter_row_ = + (dec->filter_type_ > 0) && + (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_); +} + static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { - for (dec->mb_y_ = 0; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { - VP8MB* const left = dec->mb_info_ - 1; + for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) { VP8BitReader* const token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)]; - - left->nz_ = 0; - left->dc_nz_ = 0; - memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_)); - + VP8InitScanline(dec); for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) { if (!VP8DecodeMB(dec, token_br)) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, @@ -608,11 +684,13 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { // Store data and save block's filtering params VP8StoreBlock(dec); } - if (!VP8FinishRow(dec, io)) { - return VP8SetError(dec, VP8_STATUS_USER_ABORT, - "Output aborted."); + if (!VP8ProcessRow(dec, io)) { + return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted."); } } + if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) { + return 0; + } // Finish #ifndef ONLY_KEYFRAME_CODE @@ -621,11 +699,20 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { } #endif +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dec->layer_data_size_ > 0) { + if (!VP8DecodeLayer(dec)) { + return 0; + } + } +#endif + return 1; } // Main entry point int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { + int ok = 0; if (dec == NULL) { return 0; } @@ -641,32 +728,22 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { } assert(dec->ready_); - // will allocate memory and prepare everything. - if (!VP8InitFrame(dec, io)) { - VP8Clear(dec); - return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, - "Allocation failed"); - } + // 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); - if (io->setup && !io->setup(io)) { - VP8Clear(dec); - return VP8SetError(dec, VP8_STATUS_USER_ABORT, - "Frame setup failed"); - } + // Main decoding loop + if (ok) ok = ParseFrame(dec, io); - // Disable filtering per user request (_after_ setup() is called) - if (io->bypass_filtering) dec->filter_type_ = 0; + // Exit. + ok &= VP8ExitCritical(dec, io); + } - // Main decoding loop - { - const int ret = ParseFrame(dec, io); - if (io->teardown) { - io->teardown(io); - } - if (!ret) { - VP8Clear(dec); - return 0; - } + if (!ok) { + VP8Clear(dec); + return 0; } dec->ready_ = 0; @@ -677,6 +754,9 @@ void VP8Clear(VP8Decoder* const dec) { if (dec == NULL) { return; } + if (dec->use_threads_) { + WebPWorkerEnd(&dec->worker_); + } if (dec->mem_) { free(dec->mem_); } @@ -686,7 +766,7 @@ void VP8Clear(VP8Decoder* const dec) { dec->ready_ = 0; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) } // extern "C" |