Path: blob/master/thirdparty/libwebp/src/dec/idec_dec.c
9912 views
// Copyright 2011 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Incremental decoding10//11// Author: [email protected] (Somnath Banerjee)1213#include <assert.h>14#include <string.h>15#include <stdlib.h>1617#include "src/dec/alphai_dec.h"18#include "src/dec/webpi_dec.h"19#include "src/dec/vp8_dec.h"20#include "src/dec/vp8i_dec.h"21#include "src/utils/utils.h"22#include "src/webp/decode.h"2324// In append mode, buffer allocations increase as multiples of this value.25// Needs to be a power of 2.26#define CHUNK_SIZE 409627#define MAX_MB_SIZE 40962829//------------------------------------------------------------------------------30// Data structures for memory and states3132// Decoding states. State normally flows as:33// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and34// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.35// If there is any error the decoder goes into state ERROR.36typedef enum {37STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.38STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).39STATE_VP8_PARTS0,40STATE_VP8_DATA,41STATE_VP8L_HEADER,42STATE_VP8L_DATA,43STATE_DONE,44STATE_ERROR45} DecState;4647// Operating state for the MemBuffer48typedef enum {49MEM_MODE_NONE = 0,50MEM_MODE_APPEND,51MEM_MODE_MAP52} MemBufferMode;5354// storage for partition #0 and partial data (in a rolling fashion)55typedef struct {56MemBufferMode mode_; // Operation mode57size_t start_; // start location of the data to be decoded58size_t end_; // end location59size_t buf_size_; // size of the allocated buffer60uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()6162size_t part0_size_; // size of partition #063const uint8_t* part0_buf_; // buffer to store partition #064} MemBuffer;6566struct WebPIDecoder {67DecState state_; // current decoding state68WebPDecParams params_; // Params to store output info69int is_lossless_; // for down-casting 'dec_'.70void* dec_; // either a VP8Decoder or a VP8LDecoder instance71VP8Io io_;7273MemBuffer mem_; // input memory buffer.74WebPDecBuffer output_; // output buffer (when no external one is supplied,75// or if the external one has slow-memory)76WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.77size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.7879int last_mb_y_; // last row reached for intra-mode decoding80};8182// MB context to restore in case VP8DecodeMB() fails83typedef struct {84VP8MB left_;85VP8MB info_;86VP8BitReader token_br_;87} MBContext;8889//------------------------------------------------------------------------------90// MemBuffer: incoming data handling9192static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {93return (mem->end_ - mem->start_);94}9596// Check if we need to preserve the compressed alpha data, as it may not have97// been decoded yet.98static int NeedCompressedAlpha(const WebPIDecoder* const idec) {99if (idec->state_ == STATE_WEBP_HEADER) {100// We haven't parsed the headers yet, so we don't know whether the image is101// lossy or lossless. This also means that we haven't parsed the ALPH chunk.102return 0;103}104if (idec->is_lossless_) {105return 0; // ALPH chunk is not present for lossless images.106} else {107const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;108assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER.109return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;110}111}112113static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {114MemBuffer* const mem = &idec->mem_;115const uint8_t* const new_base = mem->buf_ + mem->start_;116// note: for VP8, setting up idec->io_ is only really needed at the beginning117// of the decoding, till partition #0 is complete.118idec->io_.data = new_base;119idec->io_.data_size = MemDataSize(mem);120121if (idec->dec_ != NULL) {122if (!idec->is_lossless_) {123VP8Decoder* const dec = (VP8Decoder*)idec->dec_;124const uint32_t last_part = dec->num_parts_minus_one_;125if (offset != 0) {126uint32_t p;127for (p = 0; p <= last_part; ++p) {128VP8RemapBitReader(dec->parts_ + p, offset);129}130// Remap partition #0 data pointer to new offset, but only in MAP131// mode (in APPEND mode, partition #0 is copied into a fixed memory).132if (mem->mode_ == MEM_MODE_MAP) {133VP8RemapBitReader(&dec->br_, offset);134}135}136{137const uint8_t* const last_start = dec->parts_[last_part].buf_;138VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,139mem->buf_ + mem->end_ - last_start);140}141if (NeedCompressedAlpha(idec)) {142ALPHDecoder* const alph_dec = dec->alph_dec_;143dec->alpha_data_ += offset;144if (alph_dec != NULL && alph_dec->vp8l_dec_ != NULL) {145if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {146VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;147assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);148VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,149dec->alpha_data_ + ALPHA_HEADER_LEN,150dec->alpha_data_size_ - ALPHA_HEADER_LEN);151} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION152// Nothing special to do in this case.153}154}155}156} else { // Resize lossless bitreader157VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;158VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));159}160}161}162163// Appends data to the end of MemBuffer->buf_. It expands the allocated memory164// size if required and also updates VP8BitReader's if new memory is allocated.165WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec,166const uint8_t* const data,167size_t data_size) {168VP8Decoder* const dec = (VP8Decoder*)idec->dec_;169MemBuffer* const mem = &idec->mem_;170const int need_compressed_alpha = NeedCompressedAlpha(idec);171const uint8_t* const old_start =172(mem->buf_ == NULL) ? NULL : mem->buf_ + mem->start_;173const uint8_t* const old_base =174need_compressed_alpha ? dec->alpha_data_ : old_start;175assert(mem->buf_ != NULL || mem->start_ == 0);176assert(mem->mode_ == MEM_MODE_APPEND);177if (data_size > MAX_CHUNK_PAYLOAD) {178// security safeguard: trying to allocate more than what the format179// allows for a chunk should be considered a smoke smell.180return 0;181}182183if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory184const size_t new_mem_start = old_start - old_base;185const size_t current_size = MemDataSize(mem) + new_mem_start;186const uint64_t new_size = (uint64_t)current_size + data_size;187const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);188uint8_t* const new_buf =189(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));190if (new_buf == NULL) return 0;191if (old_base != NULL) memcpy(new_buf, old_base, current_size);192WebPSafeFree(mem->buf_);193mem->buf_ = new_buf;194mem->buf_size_ = (size_t)extra_size;195mem->start_ = new_mem_start;196mem->end_ = current_size;197}198199assert(mem->buf_ != NULL);200memcpy(mem->buf_ + mem->end_, data, data_size);201mem->end_ += data_size;202assert(mem->end_ <= mem->buf_size_);203204DoRemap(idec, mem->buf_ + mem->start_ - old_start);205return 1;206}207208WEBP_NODISCARD static int RemapMemBuffer(WebPIDecoder* const idec,209const uint8_t* const data,210size_t data_size) {211MemBuffer* const mem = &idec->mem_;212const uint8_t* const old_buf = mem->buf_;213const uint8_t* const old_start =214(old_buf == NULL) ? NULL : old_buf + mem->start_;215assert(old_buf != NULL || mem->start_ == 0);216assert(mem->mode_ == MEM_MODE_MAP);217218if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!219220mem->buf_ = (uint8_t*)data;221mem->end_ = mem->buf_size_ = data_size;222223DoRemap(idec, mem->buf_ + mem->start_ - old_start);224return 1;225}226227static void InitMemBuffer(MemBuffer* const mem) {228mem->mode_ = MEM_MODE_NONE;229mem->buf_ = NULL;230mem->buf_size_ = 0;231mem->part0_buf_ = NULL;232mem->part0_size_ = 0;233}234235static void ClearMemBuffer(MemBuffer* const mem) {236assert(mem);237if (mem->mode_ == MEM_MODE_APPEND) {238WebPSafeFree(mem->buf_);239WebPSafeFree((void*)mem->part0_buf_);240}241}242243WEBP_NODISCARD static int CheckMemBufferMode(MemBuffer* const mem,244MemBufferMode expected) {245if (mem->mode_ == MEM_MODE_NONE) {246mem->mode_ = expected; // switch to the expected mode247} else if (mem->mode_ != expected) {248return 0; // we mixed the modes => error249}250assert(mem->mode_ == expected); // mode is ok251return 1;252}253254// To be called last.255WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {256const WebPDecoderOptions* const options = idec->params_.options;257WebPDecBuffer* const output = idec->params_.output;258259idec->state_ = STATE_DONE;260if (options != NULL && options->flip) {261const VP8StatusCode status = WebPFlipBuffer(output);262if (status != VP8_STATUS_OK) return status;263}264if (idec->final_output_ != NULL) {265const VP8StatusCode status = WebPCopyDecBufferPixels(266output, idec->final_output_); // do the slow-copy267WebPFreeDecBuffer(&idec->output_);268if (status != VP8_STATUS_OK) return status;269*output = *idec->final_output_;270idec->final_output_ = NULL;271}272return VP8_STATUS_OK;273}274275//------------------------------------------------------------------------------276// Macroblock-decoding contexts277278static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,279MBContext* const context) {280context->left_ = dec->mb_info_[-1];281context->info_ = dec->mb_info_[dec->mb_x_];282context->token_br_ = *token_br;283}284285static void RestoreContext(const MBContext* context, VP8Decoder* const dec,286VP8BitReader* const token_br) {287dec->mb_info_[-1] = context->left_;288dec->mb_info_[dec->mb_x_] = context->info_;289*token_br = context->token_br_;290}291292//------------------------------------------------------------------------------293294static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {295if (idec->state_ == STATE_VP8_DATA) {296// Synchronize the thread, clean-up and check for errors.297(void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);298}299idec->state_ = STATE_ERROR;300return error;301}302303static void ChangeState(WebPIDecoder* const idec, DecState new_state,304size_t consumed_bytes) {305MemBuffer* const mem = &idec->mem_;306idec->state_ = new_state;307mem->start_ += consumed_bytes;308assert(mem->start_ <= mem->end_);309idec->io_.data = mem->buf_ + mem->start_;310idec->io_.data_size = MemDataSize(mem);311}312313// Headers314static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {315MemBuffer* const mem = &idec->mem_;316const uint8_t* data = mem->buf_ + mem->start_;317size_t curr_size = MemDataSize(mem);318VP8StatusCode status;319WebPHeaderStructure headers;320321headers.data = data;322headers.data_size = curr_size;323headers.have_all_data = 0;324status = WebPParseHeaders(&headers);325if (status == VP8_STATUS_NOT_ENOUGH_DATA) {326return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.327} else if (status != VP8_STATUS_OK) {328return IDecError(idec, status);329}330331idec->chunk_size_ = headers.compressed_size;332idec->is_lossless_ = headers.is_lossless;333if (!idec->is_lossless_) {334VP8Decoder* const dec = VP8New();335if (dec == NULL) {336return VP8_STATUS_OUT_OF_MEMORY;337}338dec->incremental_ = 1;339idec->dec_ = dec;340dec->alpha_data_ = headers.alpha_data;341dec->alpha_data_size_ = headers.alpha_data_size;342ChangeState(idec, STATE_VP8_HEADER, headers.offset);343} else {344VP8LDecoder* const dec = VP8LNew();345if (dec == NULL) {346return VP8_STATUS_OUT_OF_MEMORY;347}348idec->dec_ = dec;349ChangeState(idec, STATE_VP8L_HEADER, headers.offset);350}351return VP8_STATUS_OK;352}353354static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {355const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;356const size_t curr_size = MemDataSize(&idec->mem_);357int width, height;358uint32_t bits;359360if (curr_size < VP8_FRAME_HEADER_SIZE) {361// Not enough data bytes to extract VP8 Frame Header.362return VP8_STATUS_SUSPENDED;363}364if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) {365return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);366}367368bits = data[0] | (data[1] << 8) | (data[2] << 16);369idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;370371idec->io_.data = data;372idec->io_.data_size = curr_size;373idec->state_ = STATE_VP8_PARTS0;374return VP8_STATUS_OK;375}376377// Partition #0378static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {379VP8Decoder* const dec = (VP8Decoder*)idec->dec_;380VP8BitReader* const br = &dec->br_;381const size_t part_size = br->buf_end_ - br->buf_;382MemBuffer* const mem = &idec->mem_;383assert(!idec->is_lossless_);384assert(mem->part0_buf_ == NULL);385// the following is a format limitation, no need for runtime check:386assert(part_size <= mem->part0_size_);387if (part_size == 0) { // can't have zero-size partition #0388return VP8_STATUS_BITSTREAM_ERROR;389}390if (mem->mode_ == MEM_MODE_APPEND) {391// We copy and grab ownership of the partition #0 data.392uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);393if (part0_buf == NULL) {394return VP8_STATUS_OUT_OF_MEMORY;395}396memcpy(part0_buf, br->buf_, part_size);397mem->part0_buf_ = part0_buf;398VP8BitReaderSetBuffer(br, part0_buf, part_size);399} else {400// Else: just keep pointers to the partition #0's data in dec_->br_.401}402mem->start_ += part_size;403return VP8_STATUS_OK;404}405406static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {407VP8Decoder* const dec = (VP8Decoder*)idec->dec_;408VP8Io* const io = &idec->io_;409const WebPDecParams* const params = &idec->params_;410WebPDecBuffer* const output = params->output;411412// Wait till we have enough data for the whole partition #0413if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {414return VP8_STATUS_SUSPENDED;415}416417if (!VP8GetHeaders(dec, io)) {418const VP8StatusCode status = dec->status_;419if (status == VP8_STATUS_SUSPENDED ||420status == VP8_STATUS_NOT_ENOUGH_DATA) {421// treating NOT_ENOUGH_DATA as SUSPENDED state422return VP8_STATUS_SUSPENDED;423}424return IDecError(idec, status);425}426427// Allocate/Verify output buffer now428dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,429output);430if (dec->status_ != VP8_STATUS_OK) {431return IDecError(idec, dec->status_);432}433// This change must be done before calling VP8InitFrame()434dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,435io->width, io->height);436VP8InitDithering(params->options, dec);437438dec->status_ = CopyParts0Data(idec);439if (dec->status_ != VP8_STATUS_OK) {440return IDecError(idec, dec->status_);441}442443// Finish setting up the decoding parameters. Will call io->setup().444if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {445return IDecError(idec, dec->status_);446}447448// Note: past this point, teardown() must always be called449// in case of error.450idec->state_ = STATE_VP8_DATA;451// Allocate memory and prepare everything.452if (!VP8InitFrame(dec, io)) {453return IDecError(idec, dec->status_);454}455return VP8_STATUS_OK;456}457458// Remaining partitions459static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {460VP8Decoder* const dec = (VP8Decoder*)idec->dec_;461VP8Io* const io = &idec->io_;462463// Make sure partition #0 has been read before, to set dec to ready_.464if (!dec->ready_) {465return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);466}467for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {468if (idec->last_mb_y_ != dec->mb_y_) {469if (!VP8ParseIntraModeRow(&dec->br_, dec)) {470// note: normally, error shouldn't occur since we already have the whole471// partition0 available here in DecodeRemaining(). Reaching EOF while472// reading intra modes really means a BITSTREAM_ERROR.473return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);474}475idec->last_mb_y_ = dec->mb_y_;476}477for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {478VP8BitReader* const token_br =479&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];480MBContext context;481SaveContext(dec, token_br, &context);482if (!VP8DecodeMB(dec, token_br)) {483// We shouldn't fail when MAX_MB data was available484if (dec->num_parts_minus_one_ == 0 &&485MemDataSize(&idec->mem_) > MAX_MB_SIZE) {486return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);487}488// Synchronize the threads.489if (dec->mt_method_ > 0) {490if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) {491return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);492}493}494RestoreContext(&context, dec, token_br);495return VP8_STATUS_SUSPENDED;496}497// Release buffer only if there is only one partition498if (dec->num_parts_minus_one_ == 0) {499idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;500assert(idec->mem_.start_ <= idec->mem_.end_);501}502}503VP8InitScanline(dec); // Prepare for next scanline504505// Reconstruct, filter and emit the row.506if (!VP8ProcessRow(dec, io)) {507return IDecError(idec, VP8_STATUS_USER_ABORT);508}509}510// Synchronize the thread and check for errors.511if (!VP8ExitCritical(dec, io)) {512idec->state_ = STATE_ERROR; // prevent re-entry in IDecError513return IDecError(idec, VP8_STATUS_USER_ABORT);514}515dec->ready_ = 0;516return FinishDecoding(idec);517}518519static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,520VP8StatusCode status) {521if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {522return VP8_STATUS_SUSPENDED;523}524return IDecError(idec, status);525}526527static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {528VP8Io* const io = &idec->io_;529VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;530const WebPDecParams* const params = &idec->params_;531WebPDecBuffer* const output = params->output;532size_t curr_size = MemDataSize(&idec->mem_);533assert(idec->is_lossless_);534535// Wait until there's enough data for decoding header.536if (curr_size < (idec->chunk_size_ >> 3)) {537dec->status_ = VP8_STATUS_SUSPENDED;538return ErrorStatusLossless(idec, dec->status_);539}540541if (!VP8LDecodeHeader(dec, io)) {542if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&543curr_size < idec->chunk_size_) {544dec->status_ = VP8_STATUS_SUSPENDED;545}546return ErrorStatusLossless(idec, dec->status_);547}548// Allocate/verify output buffer now.549dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,550output);551if (dec->status_ != VP8_STATUS_OK) {552return IDecError(idec, dec->status_);553}554555idec->state_ = STATE_VP8L_DATA;556return VP8_STATUS_OK;557}558559static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {560VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;561const size_t curr_size = MemDataSize(&idec->mem_);562assert(idec->is_lossless_);563564// Switch to incremental decoding if we don't have all the bytes available.565dec->incremental_ = (curr_size < idec->chunk_size_);566567if (!VP8LDecodeImage(dec)) {568return ErrorStatusLossless(idec, dec->status_);569}570assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);571return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_572: FinishDecoding(idec);573}574575// Main decoding loop576static VP8StatusCode IDecode(WebPIDecoder* idec) {577VP8StatusCode status = VP8_STATUS_SUSPENDED;578579if (idec->state_ == STATE_WEBP_HEADER) {580status = DecodeWebPHeaders(idec);581} else {582if (idec->dec_ == NULL) {583return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.584}585}586if (idec->state_ == STATE_VP8_HEADER) {587status = DecodeVP8FrameHeader(idec);588}589if (idec->state_ == STATE_VP8_PARTS0) {590status = DecodePartition0(idec);591}592if (idec->state_ == STATE_VP8_DATA) {593const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;594if (dec == NULL) {595return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.596}597status = DecodeRemaining(idec);598}599if (idec->state_ == STATE_VP8L_HEADER) {600status = DecodeVP8LHeader(idec);601}602if (idec->state_ == STATE_VP8L_DATA) {603status = DecodeVP8LData(idec);604}605return status;606}607608//------------------------------------------------------------------------------609// Internal constructor610611WEBP_NODISCARD static WebPIDecoder* NewDecoder(612WebPDecBuffer* const output_buffer,613const WebPBitstreamFeatures* const features) {614WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));615if (idec == NULL) {616return NULL;617}618619idec->state_ = STATE_WEBP_HEADER;620idec->chunk_size_ = 0;621622idec->last_mb_y_ = -1;623624InitMemBuffer(&idec->mem_);625if (!WebPInitDecBuffer(&idec->output_) || !VP8InitIo(&idec->io_)) {626WebPSafeFree(idec);627return NULL;628}629630WebPResetDecParams(&idec->params_);631if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {632idec->params_.output = &idec->output_;633idec->final_output_ = output_buffer;634if (output_buffer != NULL) {635idec->params_.output->colorspace = output_buffer->colorspace;636}637} else {638idec->params_.output = output_buffer;639idec->final_output_ = NULL;640}641WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.642643return idec;644}645646//------------------------------------------------------------------------------647// Public functions648649WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {650return NewDecoder(output_buffer, NULL);651}652653WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,654WebPDecoderConfig* config) {655WebPIDecoder* idec;656WebPBitstreamFeatures tmp_features;657WebPBitstreamFeatures* const features =658(config == NULL) ? &tmp_features : &config->input;659memset(&tmp_features, 0, sizeof(tmp_features));660661// Parse the bitstream's features, if requested:662if (data != NULL && data_size > 0) {663if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {664return NULL;665}666}667668// Create an instance of the incremental decoder669idec = (config != NULL) ? NewDecoder(&config->output, features)670: NewDecoder(NULL, features);671if (idec == NULL) {672return NULL;673}674// Finish initialization675if (config != NULL) {676idec->params_.options = &config->options;677}678return idec;679}680681void WebPIDelete(WebPIDecoder* idec) {682if (idec == NULL) return;683if (idec->dec_ != NULL) {684if (!idec->is_lossless_) {685if (idec->state_ == STATE_VP8_DATA) {686// Synchronize the thread, clean-up and check for errors.687// TODO(vrabaud) do we care about the return result?688(void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);689}690VP8Delete((VP8Decoder*)idec->dec_);691} else {692VP8LDelete((VP8LDecoder*)idec->dec_);693}694}695ClearMemBuffer(&idec->mem_);696WebPFreeDecBuffer(&idec->output_);697WebPSafeFree(idec);698}699700//------------------------------------------------------------------------------701// Wrapper toward WebPINewDecoder702703WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer,704size_t output_buffer_size, int output_stride) {705const int is_external_memory = (output_buffer != NULL) ? 1 : 0;706WebPIDecoder* idec;707708if (csp >= MODE_YUV) return NULL;709if (is_external_memory == 0) { // Overwrite parameters to sane values.710output_buffer_size = 0;711output_stride = 0;712} else { // A buffer was passed. Validate the other params.713if (output_stride == 0 || output_buffer_size == 0) {714return NULL; // invalid parameter.715}716}717idec = WebPINewDecoder(NULL);718if (idec == NULL) return NULL;719idec->output_.colorspace = csp;720idec->output_.is_external_memory = is_external_memory;721idec->output_.u.RGBA.rgba = output_buffer;722idec->output_.u.RGBA.stride = output_stride;723idec->output_.u.RGBA.size = output_buffer_size;724return idec;725}726727WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,728uint8_t* u, size_t u_size, int u_stride,729uint8_t* v, size_t v_size, int v_stride,730uint8_t* a, size_t a_size, int a_stride) {731const int is_external_memory = (luma != NULL) ? 1 : 0;732WebPIDecoder* idec;733WEBP_CSP_MODE colorspace;734735if (is_external_memory == 0) { // Overwrite parameters to sane values.736luma_size = u_size = v_size = a_size = 0;737luma_stride = u_stride = v_stride = a_stride = 0;738u = v = a = NULL;739colorspace = MODE_YUVA;740} else { // A luma buffer was passed. Validate the other parameters.741if (u == NULL || v == NULL) return NULL;742if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL;743if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL;744if (a != NULL) {745if (a_size == 0 || a_stride == 0) return NULL;746}747colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;748}749750idec = WebPINewDecoder(NULL);751if (idec == NULL) return NULL;752753idec->output_.colorspace = colorspace;754idec->output_.is_external_memory = is_external_memory;755idec->output_.u.YUVA.y = luma;756idec->output_.u.YUVA.y_stride = luma_stride;757idec->output_.u.YUVA.y_size = luma_size;758idec->output_.u.YUVA.u = u;759idec->output_.u.YUVA.u_stride = u_stride;760idec->output_.u.YUVA.u_size = u_size;761idec->output_.u.YUVA.v = v;762idec->output_.u.YUVA.v_stride = v_stride;763idec->output_.u.YUVA.v_size = v_size;764idec->output_.u.YUVA.a = a;765idec->output_.u.YUVA.a_stride = a_stride;766idec->output_.u.YUVA.a_size = a_size;767return idec;768}769770WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,771uint8_t* u, size_t u_size, int u_stride,772uint8_t* v, size_t v_size, int v_stride) {773return WebPINewYUVA(luma, luma_size, luma_stride,774u, u_size, u_stride,775v, v_size, v_stride,776NULL, 0, 0);777}778779//------------------------------------------------------------------------------780781static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {782assert(idec);783if (idec->state_ == STATE_ERROR) {784return VP8_STATUS_BITSTREAM_ERROR;785}786if (idec->state_ == STATE_DONE) {787return VP8_STATUS_OK;788}789return VP8_STATUS_SUSPENDED;790}791792VP8StatusCode WebPIAppend(WebPIDecoder* idec,793const uint8_t* data, size_t data_size) {794VP8StatusCode status;795if (idec == NULL || data == NULL) {796return VP8_STATUS_INVALID_PARAM;797}798status = IDecCheckStatus(idec);799if (status != VP8_STATUS_SUSPENDED) {800return status;801}802// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.803if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {804return VP8_STATUS_INVALID_PARAM;805}806// Append data to memory buffer807if (!AppendToMemBuffer(idec, data, data_size)) {808return VP8_STATUS_OUT_OF_MEMORY;809}810return IDecode(idec);811}812813VP8StatusCode WebPIUpdate(WebPIDecoder* idec,814const uint8_t* data, size_t data_size) {815VP8StatusCode status;816if (idec == NULL || data == NULL) {817return VP8_STATUS_INVALID_PARAM;818}819status = IDecCheckStatus(idec);820if (status != VP8_STATUS_SUSPENDED) {821return status;822}823// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.824if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {825return VP8_STATUS_INVALID_PARAM;826}827// Make the memory buffer point to the new buffer828if (!RemapMemBuffer(idec, data, data_size)) {829return VP8_STATUS_INVALID_PARAM;830}831return IDecode(idec);832}833834//------------------------------------------------------------------------------835836static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {837if (idec == NULL || idec->dec_ == NULL) {838return NULL;839}840if (idec->state_ <= STATE_VP8_PARTS0) {841return NULL;842}843if (idec->final_output_ != NULL) {844return NULL; // not yet slow-copied845}846return idec->params_.output;847}848849const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,850int* left, int* top,851int* width, int* height) {852const WebPDecBuffer* const src = GetOutputBuffer(idec);853if (left != NULL) *left = 0;854if (top != NULL) *top = 0;855if (src != NULL) {856if (width != NULL) *width = src->width;857if (height != NULL) *height = idec->params_.last_y;858} else {859if (width != NULL) *width = 0;860if (height != NULL) *height = 0;861}862return src;863}864865WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,866int* width, int* height, int* stride) {867const WebPDecBuffer* const src = GetOutputBuffer(idec);868if (src == NULL) return NULL;869if (src->colorspace >= MODE_YUV) {870return NULL;871}872873if (last_y != NULL) *last_y = idec->params_.last_y;874if (width != NULL) *width = src->width;875if (height != NULL) *height = src->height;876if (stride != NULL) *stride = src->u.RGBA.stride;877878return src->u.RGBA.rgba;879}880881WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,882uint8_t** u, uint8_t** v, uint8_t** a,883int* width, int* height, int* stride,884int* uv_stride, int* a_stride) {885const WebPDecBuffer* const src = GetOutputBuffer(idec);886if (src == NULL) return NULL;887if (src->colorspace < MODE_YUV) {888return NULL;889}890891if (last_y != NULL) *last_y = idec->params_.last_y;892if (u != NULL) *u = src->u.YUVA.u;893if (v != NULL) *v = src->u.YUVA.v;894if (a != NULL) *a = src->u.YUVA.a;895if (width != NULL) *width = src->width;896if (height != NULL) *height = src->height;897if (stride != NULL) *stride = src->u.YUVA.y_stride;898if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;899if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;900901return src->u.YUVA.y;902}903904int WebPISetIOHooks(WebPIDecoder* const idec,905VP8IoPutHook put,906VP8IoSetupHook setup,907VP8IoTeardownHook teardown,908void* user_data) {909if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) {910return 0;911}912913idec->io_.put = put;914idec->io_.setup = setup;915idec->io_.teardown = teardown;916idec->io_.opaque = user_data;917918return 1;919}920921922