mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 02:15:42 +01:00 
			
		
		
		
	add incremental decoding
The object WebPIDecoder is available to store the
decoding state. The flow is typically:
   WebPIDecoder* const idec = WebPINew(mode);
   while (has_more_data) {
     // ... (get additional data)
     status = WebPIAppend(idec, new_data, new_data_size);
     if (status != VP8_STATUS_SUSPENDED ||
       break;
     }
     // The above call decodes the current available buffer.
     // Part of the image can now be refreshed by calling to
     // WebPIDecGetRGB()/WebPIDecGetYUV() etc.
   }
   WebPIDelete(idec);
Doing so, one can try and decode new macroblocks everytime fresh
bytes are available.
There's two operating modes: either appending fresh bytes, or
updating the whole buffer with additional data in the end.
The latter requires less memcpy()'s
main patch by Somnath Banerjee (somnath at google.com)
Change-Id: Ie81cbd0b50f175743af06b1f964de838b9a10a4a
			
			
This commit is contained in:
		| @@ -44,7 +44,8 @@ OBJS = src/enc/webpenc.o src/enc/bit_writer.o src/enc/syntax.o \ | |||||||
|        src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \ |        src/enc/quant.o src/enc/iterator.o src/enc/analysis.o \ | ||||||
|        src/enc/cost.o src/enc/picture.o src/enc/filter.o \ |        src/enc/cost.o src/enc/picture.o src/enc/filter.o \ | ||||||
|        src/dec/bits.o src/dec/dsp.o src/dec/frame.o src/dec/webp.o \ |        src/dec/bits.o src/dec/dsp.o src/dec/frame.o src/dec/webp.o \ | ||||||
|        src/dec/quant.o src/dec/tree.o src/dec/vp8.o src/dec/yuv.o |        src/dec/quant.o src/dec/tree.o src/dec/vp8.o src/dec/yuv.o \ | ||||||
|  |        src/dec/idec.o | ||||||
| HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/bit_writer.h \ | HDRS = src/webp/encode.h src/enc/vp8enci.h src/enc/bit_writer.h \ | ||||||
|        src/enc/cost.h src/dec/bits.h  src/dec/vp8i.h src/dec/yuv.h |        src/enc/cost.h src/dec/bits.h  src/dec/vp8i.h src/dec/yuv.h | ||||||
| OUTPUT = examples/cwebp examples/dwebp src/libwebp.a | OUTPUT = examples/cwebp examples/dwebp src/libwebp.a | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| AM_CPPFLAGS = -I$(top_srcdir)/src | AM_CPPFLAGS = -I$(top_srcdir)/src | ||||||
|  |  | ||||||
| libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ | libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ | ||||||
|                           quant.c tree.c vp8.c webp.c yuv.c |                           quant.c tree.c vp8.c webp.c yuv.c idec.c | ||||||
| libwebpdecode_la_LDFLAGS = -version-info 0:0:0 | libwebpdecode_la_LDFLAGS = -version-info 0:0:0 | ||||||
| libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h | libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h | ||||||
| libwebpdecodeincludedir = $(includedir)/webp | libwebpdecodeincludedir = $(includedir)/webp | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ CONFIG_CLEAN_VPATH_FILES = | |||||||
| LTLIBRARIES = $(noinst_LTLIBRARIES) | LTLIBRARIES = $(noinst_LTLIBRARIES) | ||||||
| libwebpdecode_la_LIBADD = | libwebpdecode_la_LIBADD = | ||||||
| am_libwebpdecode_la_OBJECTS = bits.lo dsp.lo frame.lo quant.lo tree.lo \ | am_libwebpdecode_la_OBJECTS = bits.lo dsp.lo frame.lo quant.lo tree.lo \ | ||||||
| 	vp8.lo webp.lo yuv.lo | 	vp8.lo webp.lo yuv.lo idec.lo | ||||||
| libwebpdecode_la_OBJECTS = $(am_libwebpdecode_la_OBJECTS) | libwebpdecode_la_OBJECTS = $(am_libwebpdecode_la_OBJECTS) | ||||||
| libwebpdecode_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ | libwebpdecode_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ | ||||||
| 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ | 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ | ||||||
| @@ -213,7 +213,7 @@ top_builddir = @top_builddir@ | |||||||
| top_srcdir = @top_srcdir@ | top_srcdir = @top_srcdir@ | ||||||
| AM_CPPFLAGS = -I$(top_srcdir)/src | AM_CPPFLAGS = -I$(top_srcdir)/src | ||||||
| libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ | libwebpdecode_la_SOURCES = bits.h vp8i.h yuv.h bits.c dsp.c frame.c \ | ||||||
|                           quant.c tree.c vp8.c webp.c yuv.c |                           quant.c tree.c vp8.c webp.c yuv.c idec.c | ||||||
|  |  | ||||||
| libwebpdecode_la_LDFLAGS = -version-info 0:0:0 | libwebpdecode_la_LDFLAGS = -version-info 0:0:0 | ||||||
| libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h | libwebpdecodeinclude_HEADERS = ../webp/decode.h ../webp/decode_vp8.h ../webp/types.h | ||||||
| @@ -275,6 +275,7 @@ distclean-compile: | |||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bits.Plo@am__quote@ | ||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp.Plo@am__quote@ | ||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frame.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frame.Plo@am__quote@ | ||||||
|  | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idec.Plo@am__quote@ | ||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quant.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quant.Plo@am__quote@ | ||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Plo@am__quote@ | ||||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vp8.Plo@am__quote@ | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vp8.Plo@am__quote@ | ||||||
|   | |||||||
							
								
								
									
										567
									
								
								src/dec/idec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								src/dec/idec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,567 @@ | |||||||
|  | // Copyright 2011 Google Inc. | ||||||
|  | // | ||||||
|  | // This code is licensed under the same terms as WebM: | ||||||
|  | //  Software License Agreement:  http://www.webmproject.org/license/software/ | ||||||
|  | //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/ | ||||||
|  | // ----------------------------------------------------------------------------- | ||||||
|  | // | ||||||
|  | // Incremental decoding | ||||||
|  | // | ||||||
|  | // Author: somnath@google.com (Somnath Banerjee) | ||||||
|  |  | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | #include "webpi.h" | ||||||
|  | #include "vp8i.h" | ||||||
|  | #include "yuv.h" | ||||||
|  |  | ||||||
|  | #define RIFF_HEADER_SIZE 20 | ||||||
|  | #define VP8_HEADER_SIZE 10 | ||||||
|  | #define WEBP_HEADER_SIZE (RIFF_HEADER_SIZE + VP8_HEADER_SIZE) | ||||||
|  | #define CHUNK_SIZE 4096 | ||||||
|  | #define MAX_MB_SIZE 4096 | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // Data structures for memory and states | ||||||
|  |  | ||||||
|  | // Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE. | ||||||
|  | // If there is any error the decoder goes into state ERROR. | ||||||
|  | typedef enum { STATE_HEADER = 0, STATE_PARTS0 = 1, | ||||||
|  |                STATE_DATA = 2, STATE_DONE = 3, | ||||||
|  |                STATE_ERROR = 4 | ||||||
|  | } DecState; | ||||||
|  |  | ||||||
|  | // Operating state for the MemBuffer | ||||||
|  | typedef enum { MEM_MODE_NONE = 0, | ||||||
|  |                MEM_MODE_APPEND, MEM_MODE_MAP | ||||||
|  | } MemBufferMode; | ||||||
|  |  | ||||||
|  | // storage for partition #0 and partial data (in a rolling fashion) | ||||||
|  | typedef struct { | ||||||
|  |   MemBufferMode mode_;  // Operation mode | ||||||
|  |   uint32_t start_;      // start location of the data to be decoded | ||||||
|  |   uint32_t end_;        // end location | ||||||
|  |   size_t buf_size_;     // size of the allocated buffer | ||||||
|  |   uint8_t* buf_;        // We don't own this buffer in case WebPIUpdate() | ||||||
|  |  | ||||||
|  |   size_t part0_size_;         // size of partition #0 | ||||||
|  |   const uint8_t* part0_buf_;  // buffer to store partition #0 | ||||||
|  | } MemBuffer; | ||||||
|  |  | ||||||
|  | struct WebPIDecoder { | ||||||
|  |   DecState state_;         // current decoding state | ||||||
|  |   int w_, h_;              // width and height | ||||||
|  |   WebPDecParams params_;   // Params to store output info | ||||||
|  |   VP8Decoder* dec_; | ||||||
|  |   VP8Io io_; | ||||||
|  |  | ||||||
|  |   MemBuffer mem_;          // memory buffer | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // MB context to restore in case VP8DecodeMB() fails | ||||||
|  | typedef struct { | ||||||
|  |   VP8MB left_; | ||||||
|  |   VP8MB info_; | ||||||
|  |   uint8_t intra_t_[4]; | ||||||
|  |   uint8_t intra_l_[4]; | ||||||
|  |   VP8BitReader br_; | ||||||
|  |   VP8BitReader token_br_; | ||||||
|  | } MBContext; | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // MemBuffer: incoming data handling | ||||||
|  |  | ||||||
|  | #define REMAP(PTR, OLD_BASE, NEW_BASE) (PTR) = (NEW_BASE) + ((PTR) - OLD_BASE) | ||||||
|  |  | ||||||
|  | static inline size_t MemDataSize(const MemBuffer* mem) { | ||||||
|  |   return (mem->end_ - mem->start_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Appends data to the end of MemBuffer->buf_. It expands the allocated memory | ||||||
|  | // size if required and also updates VP8BitReader's if new memory is allocated. | ||||||
|  | static int AppendToMemBuffer(WebPIDecoder* const idec, | ||||||
|  |                              const uint8_t* const data, size_t data_size) { | ||||||
|  |   MemBuffer* const mem = &idec->mem_; | ||||||
|  |   VP8Decoder* const dec = idec->dec_; | ||||||
|  |   const int last_part = dec->num_parts_ - 1; | ||||||
|  |   assert(mem->mode_ == MEM_MODE_APPEND); | ||||||
|  |  | ||||||
|  |   if (mem->end_ + data_size > mem->buf_size_) {  // Need some free memory | ||||||
|  |     int p; | ||||||
|  |     uint8_t* new_buf = NULL; | ||||||
|  |     const int num_chunks = (MemDataSize(mem) + data_size + CHUNK_SIZE - 1) | ||||||
|  |         / CHUNK_SIZE; | ||||||
|  |     const size_t new_size = num_chunks * CHUNK_SIZE; | ||||||
|  |     const uint8_t* const base = mem->buf_ + mem->start_; | ||||||
|  |  | ||||||
|  |     new_buf = (uint8_t*)malloc(new_size); | ||||||
|  |     if (!new_buf) return 0; | ||||||
|  |     memcpy(new_buf, base, MemDataSize(mem)); | ||||||
|  |  | ||||||
|  |     // adjust VP8BitReader pointers | ||||||
|  |     for (p = 0; p <= last_part; ++p) { | ||||||
|  |       if (dec->parts_[p].buf_) { | ||||||
|  |         REMAP(dec->parts_[p].buf_, base, new_buf); | ||||||
|  |         REMAP(dec->parts_[p].buf_end_, base, new_buf); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // adjust memory pointers | ||||||
|  |     free(mem->buf_); | ||||||
|  |     mem->buf_ = new_buf; | ||||||
|  |     mem->buf_size_ = new_size; | ||||||
|  |  | ||||||
|  |     mem->end_ = MemDataSize(mem); | ||||||
|  |     mem->start_ = 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   memcpy(mem->buf_ + mem->end_, data, data_size); | ||||||
|  |   mem->end_ += data_size; | ||||||
|  |   assert(mem->end_ <= mem->buf_size_); | ||||||
|  |   dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_; | ||||||
|  |  | ||||||
|  |   // note: setting up idec->io_ is only really needed at the beginning | ||||||
|  |   // of the decoding, till partition #0 is complete. | ||||||
|  |   idec->io_.data = mem->buf_ + mem->start_; | ||||||
|  |   idec->io_.data_size = MemDataSize(mem); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int RemapMemBuffer(WebPIDecoder* const idec, | ||||||
|  |                           const uint8_t* const data, size_t data_size) { | ||||||
|  |   int p; | ||||||
|  |   MemBuffer* const mem = &idec->mem_; | ||||||
|  |   VP8Decoder* const dec = idec->dec_; | ||||||
|  |   const int last_part = dec->num_parts_ - 1; | ||||||
|  |   const uint8_t* base = mem->buf_; | ||||||
|  |  | ||||||
|  |   assert(mem->mode_ == MEM_MODE_MAP); | ||||||
|  |   if (data_size < mem->buf_size_) { | ||||||
|  |     return 0;  // we cannot remap to a shorter buffer! | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   for (p = 0; p <= last_part; ++p) { | ||||||
|  |     if (dec->parts_[p].buf_) { | ||||||
|  |       REMAP(dec->parts_[p].buf_, base, data); | ||||||
|  |       REMAP(dec->parts_[p].buf_end_, base, data); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   dec->parts_[last_part].buf_end_ = data + data_size; | ||||||
|  |  | ||||||
|  |   // Remap partition #0 data pointer to new offset. | ||||||
|  |   if (dec->br_.buf_) { | ||||||
|  |     REMAP(dec->br_.buf_, base, data); | ||||||
|  |     REMAP(dec->br_.buf_end_, base, data); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   mem->buf_ = (uint8_t*)data; | ||||||
|  |   mem->end_ = mem->buf_size_ = data_size; | ||||||
|  |  | ||||||
|  |   idec->io_.data = data; | ||||||
|  |   idec->io_.data_size = data_size; | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void InitMemBuffer(MemBuffer* const mem) { | ||||||
|  |   mem->mode_       = MEM_MODE_NONE; | ||||||
|  |   mem->buf_        = 0; | ||||||
|  |   mem->buf_size_   = 0; | ||||||
|  |   mem->part0_buf_  = 0; | ||||||
|  |   mem->part0_size_ = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void ClearMemBuffer(MemBuffer* const mem) { | ||||||
|  |   assert(mem); | ||||||
|  |   if (mem->mode_ == MEM_MODE_APPEND) { | ||||||
|  |     free(mem->buf_); | ||||||
|  |     free((void*)mem->part0_buf_); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { | ||||||
|  |   if (mem->mode_ == MEM_MODE_NONE) { | ||||||
|  |     mem->mode_ = expected;    // switch to the expected mode | ||||||
|  |   } else if (mem->mode_ != expected) { | ||||||
|  |     return 0;         // we mixed the modes => error | ||||||
|  |   } | ||||||
|  |   assert(mem->mode_ == expected);   // mode is ok | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #undef REMAP | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // Macroblock-decoding contexts | ||||||
|  |  | ||||||
|  | static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, | ||||||
|  |                         MBContext* const context) { | ||||||
|  |   const VP8BitReader* const br = &dec->br_; | ||||||
|  |   const VP8MB* const left = dec->mb_info_ - 1; | ||||||
|  |   const VP8MB* const info = dec->mb_info_ + dec->mb_x_; | ||||||
|  |  | ||||||
|  |   context->left_ = *left; | ||||||
|  |   context->info_ = *info; | ||||||
|  |   context->br_ = *br; | ||||||
|  |   context->token_br_ = *token_br; | ||||||
|  |   memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4); | ||||||
|  |   memcpy(context->intra_l_, dec->intra_l_, 4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void RestoreContext(const MBContext* context, VP8Decoder* const dec, | ||||||
|  |                            VP8BitReader* const token_br) { | ||||||
|  |   VP8BitReader* const br = &dec->br_; | ||||||
|  |   VP8MB* const left = dec->mb_info_ - 1; | ||||||
|  |   VP8MB* const info = dec->mb_info_ + dec->mb_x_; | ||||||
|  |  | ||||||
|  |   *left = context->left_; | ||||||
|  |   *info = context->info_; | ||||||
|  |   *br = context->br_; | ||||||
|  |   *token_br = context->token_br_; | ||||||
|  |   memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4); | ||||||
|  |   memcpy(dec->intra_l_, context->intra_l_, 4); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | static VP8StatusCode IDecError(WebPIDecoder* idec, VP8StatusCode error) { | ||||||
|  |   idec->state_ = STATE_ERROR; | ||||||
|  |   return error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Header | ||||||
|  | static VP8StatusCode DecodeHeader(WebPIDecoder* const idec) { | ||||||
|  |   int width, height; | ||||||
|  |   uint32_t curr_size, riff_header_size, bits; | ||||||
|  |   WebPDecParams* params = &idec->params_; | ||||||
|  |   const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; | ||||||
|  |  | ||||||
|  |   if (MemDataSize(&idec->mem_) < WEBP_HEADER_SIZE) { | ||||||
|  |     return VP8_STATUS_SUSPENDED; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!WebPInitDecParams(data, idec->mem_.end_, &width, &height, params)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Validate and Skip over RIFF header | ||||||
|  |   curr_size = MemDataSize(&idec->mem_); | ||||||
|  |   if (!WebPCheckRIFFHeader(&data, &curr_size)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | ||||||
|  |   } | ||||||
|  |   riff_header_size = idec->mem_.end_ - curr_size; | ||||||
|  |   bits = data[0] | (data[1] << 8) | (data[2] << 16); | ||||||
|  |  | ||||||
|  |   idec->mem_.part0_size_ = (bits >> 5) + VP8_HEADER_SIZE; | ||||||
|  |   idec->mem_.start_ += riff_header_size; | ||||||
|  |   assert(idec->mem_.start_ <= idec->mem_.end_); | ||||||
|  |  | ||||||
|  |   idec->w_ = width; | ||||||
|  |   idec->h_ = height; | ||||||
|  |   idec->io_.data_size -= riff_header_size; | ||||||
|  |   idec->io_.data = data; | ||||||
|  |   idec->state_ = STATE_PARTS0; | ||||||
|  |   return VP8_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Partition #0 | ||||||
|  | static int CopyParts0Data(WebPIDecoder* idec) { | ||||||
|  |   VP8BitReader* const br = &idec->dec_->br_; | ||||||
|  |   const size_t psize = br->buf_end_ - br->buf_; | ||||||
|  |   MemBuffer* const mem = &idec->mem_; | ||||||
|  |   assert(!mem->part0_buf_); | ||||||
|  |   assert(psize > 0); | ||||||
|  |   assert(psize <= mem->part0_size_); | ||||||
|  |   if (mem->mode_ == MEM_MODE_APPEND) { | ||||||
|  |     // We copy and grab ownership of the partition #0 data. | ||||||
|  |     uint8_t* const part0_buf = (uint8_t*)malloc(psize); | ||||||
|  |     if (!part0_buf) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |     memcpy(part0_buf, br->buf_, psize); | ||||||
|  |     mem->part0_buf_ = part0_buf; | ||||||
|  |     mem->start_ += psize; | ||||||
|  |     br->buf_ = part0_buf; | ||||||
|  |     br->buf_end_ = part0_buf + psize; | ||||||
|  |   } else { | ||||||
|  |     // Else: just keep pointers to the partition #0's data in dec_->br_. | ||||||
|  |   } | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { | ||||||
|  |   VP8Decoder* const dec = idec->dec_; | ||||||
|  |   VP8Io* const io = &idec->io_; | ||||||
|  |   const WebPDecParams* const params = &idec->params_; | ||||||
|  |   const WEBP_CSP_MODE mode = params->mode; | ||||||
|  |  | ||||||
|  |   // Wait till we have enough data for the whole partition #0 | ||||||
|  |   if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) { | ||||||
|  |     return VP8_STATUS_SUSPENDED; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   io->opaque = &idec->params_; | ||||||
|  |   if (!VP8GetHeaders(dec, io)) { | ||||||
|  |     const VP8StatusCode status = dec->status_; | ||||||
|  |     if (status == VP8_STATUS_SUSPENDED || | ||||||
|  |         status == VP8_STATUS_NOT_ENOUGH_DATA) { | ||||||
|  |       // treating NOT_ENOUGH_DATA as SUSPENDED state | ||||||
|  |       return VP8_STATUS_SUSPENDED; | ||||||
|  |     } | ||||||
|  |     return IDecError(idec, status); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!WebPCheckDecParams(io, params)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_INVALID_PARAM); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (mode != MODE_YUV) { | ||||||
|  |     VP8YUVInit(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // allocate memory and prepare everything. | ||||||
|  |   if (!VP8InitFrame(dec, io)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY); | ||||||
|  |   } | ||||||
|  |   if (io->setup && !io->setup(io)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_USER_ABORT); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // disable filtering per user request (_after_ setup() is called) | ||||||
|  |   if (io->bypass_filtering) dec->filter_type_ = 0; | ||||||
|  |  | ||||||
|  |   if (!CopyParts0Data(idec)) { | ||||||
|  |     return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   idec->state_ = STATE_DATA; | ||||||
|  |   return VP8_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Remaining partitions | ||||||
|  | static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { | ||||||
|  |   VP8BitReader*  br; | ||||||
|  |   VP8Decoder* const dec = idec->dec_; | ||||||
|  |   VP8Io* const io = &idec->io_; | ||||||
|  |  | ||||||
|  |   assert(dec->ready_); | ||||||
|  |  | ||||||
|  |   br = &dec->br_; | ||||||
|  |   for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { | ||||||
|  |     VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)]; | ||||||
|  |     if (dec->mb_x_ == 0) { | ||||||
|  |       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_)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (; dec->mb_x_ < dec->mb_w_;  dec->mb_x_++) { | ||||||
|  |       MBContext context; | ||||||
|  |       SaveContext(dec, token_br, &context); | ||||||
|  |  | ||||||
|  |       if (!VP8DecodeMB(dec, token_br)) { | ||||||
|  |         RestoreContext(&context, dec, token_br); | ||||||
|  |         // We shouldn't fail when MAX_MB data was available | ||||||
|  |         if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) { | ||||||
|  |           return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); | ||||||
|  |         } | ||||||
|  |         return VP8_STATUS_SUSPENDED; | ||||||
|  |       } | ||||||
|  |       VP8ReconstructBlock(dec); | ||||||
|  |       // Store data and save block's filtering params | ||||||
|  |       VP8StoreBlock(dec); | ||||||
|  |  | ||||||
|  |       // Release buffer only if there is only one partition | ||||||
|  |       if (dec->num_parts_ == 1) { | ||||||
|  |         idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; | ||||||
|  |         assert(idec->mem_.start_ <= idec->mem_.end_); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!VP8FinishRow(dec, io)) { | ||||||
|  |       return IDecError(idec, VP8_STATUS_USER_ABORT); | ||||||
|  |     } | ||||||
|  |     dec->mb_x_ = 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (io->teardown) { | ||||||
|  |     io->teardown(io); | ||||||
|  |   } | ||||||
|  |   dec->ready_ = 0; | ||||||
|  |   idec->state_ = STATE_DONE; | ||||||
|  |  | ||||||
|  |   return VP8_STATUS_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |   // Main decoding loop | ||||||
|  | static VP8StatusCode IDecode(WebPIDecoder* idec) { | ||||||
|  |   VP8StatusCode status = VP8_STATUS_SUSPENDED; | ||||||
|  |   assert(idec->dec_); | ||||||
|  |  | ||||||
|  |   if (idec->state_ == STATE_HEADER) { | ||||||
|  |     status = DecodeHeader(idec); | ||||||
|  |   } | ||||||
|  |   if (idec->state_ == STATE_PARTS0) { | ||||||
|  |     status = DecodePartition0(idec); | ||||||
|  |   } | ||||||
|  |   if (idec->state_ == STATE_DATA) { | ||||||
|  |     return DecodeRemaining(idec); | ||||||
|  |   } | ||||||
|  |   return status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | // Public functions | ||||||
|  |  | ||||||
|  | WebPIDecoder* WebPINew(WEBP_CSP_MODE mode) { | ||||||
|  |   WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(WebPIDecoder)); | ||||||
|  |   if (!idec) return NULL; | ||||||
|  |  | ||||||
|  |   idec->dec_ = VP8New(); | ||||||
|  |   if (idec->dec_ == NULL) { | ||||||
|  |     free(idec); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   idec->state_ = STATE_HEADER; | ||||||
|  |   idec->params_.mode = mode; | ||||||
|  |  | ||||||
|  |   InitMemBuffer(&idec->mem_); | ||||||
|  |   VP8InitIo(&idec->io_); | ||||||
|  |   WebPInitCustomIo(&idec->io_); | ||||||
|  |   return idec; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void WebPIDelete(WebPIDecoder* const idec) { | ||||||
|  |   if (!idec) return; | ||||||
|  |   VP8Delete(idec->dec_); | ||||||
|  |   WebPClearDecParams(&idec->params_); | ||||||
|  |   ClearMemBuffer(&idec->mem_); | ||||||
|  |   free(idec); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, | ||||||
|  |                           int output_buffer_size, int output_stride) { | ||||||
|  |   WebPIDecoder* idec; | ||||||
|  |   if (mode == MODE_YUV) return NULL; | ||||||
|  |   idec = WebPINew(mode); | ||||||
|  |   if (idec == NULL) return NULL; | ||||||
|  |   idec->params_.output = output_buffer; | ||||||
|  |   idec->params_.stride = output_stride; | ||||||
|  |   idec->params_.output_size = output_buffer_size; | ||||||
|  |   idec->params_.external_buffer = 1; | ||||||
|  |   return idec; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride, | ||||||
|  |                           uint8_t* u, int u_size, int u_stride, | ||||||
|  |                           uint8_t* v, int v_size, int v_stride) { | ||||||
|  |   WebPIDecoder* idec = WebPINew(MODE_YUV); | ||||||
|  |   if (idec == NULL) return NULL; | ||||||
|  |   idec->params_.output = luma; | ||||||
|  |   idec->params_.stride = luma_stride; | ||||||
|  |   idec->params_.output_size = luma_size; | ||||||
|  |   idec->params_.u = u; | ||||||
|  |   idec->params_.u_stride = u_stride; | ||||||
|  |   idec->params_.output_u_size = u_size; | ||||||
|  |   idec->params_.v = v; | ||||||
|  |   idec->params_.v_stride = v_stride; | ||||||
|  |   idec->params_.output_v_size = v_size; | ||||||
|  |   idec->params_.external_buffer = 1; | ||||||
|  |   return idec; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { | ||||||
|  |   assert(idec); | ||||||
|  |   if (idec->dec_ == NULL) { | ||||||
|  |     return VP8_STATUS_USER_ABORT; | ||||||
|  |   } | ||||||
|  |   if (idec->state_ == STATE_ERROR) { | ||||||
|  |     return VP8_STATUS_BITSTREAM_ERROR; | ||||||
|  |   } | ||||||
|  |   if (idec->state_ == STATE_DONE) { | ||||||
|  |     return VP8_STATUS_OK; | ||||||
|  |   } | ||||||
|  |   return VP8_STATUS_SUSPENDED; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data, | ||||||
|  |                           uint32_t data_size) { | ||||||
|  |   if (idec == NULL || data == NULL) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   const VP8StatusCode status = IDecCheckStatus(idec); | ||||||
|  |   if (status != VP8_STATUS_SUSPENDED) { | ||||||
|  |     return status; | ||||||
|  |   } | ||||||
|  |   // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. | ||||||
|  |   if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   // Append data to memory buffer | ||||||
|  |   if (!AppendToMemBuffer(idec, data, data_size)) { | ||||||
|  |     return VP8_STATUS_OUT_OF_MEMORY; | ||||||
|  |   } | ||||||
|  |   return IDecode(idec); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data, | ||||||
|  |                           uint32_t data_size) { | ||||||
|  |   if (idec == NULL || data == NULL) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   const VP8StatusCode status = IDecCheckStatus(idec); | ||||||
|  |   if (status != VP8_STATUS_SUSPENDED) { | ||||||
|  |     return status; | ||||||
|  |   } | ||||||
|  |   // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. | ||||||
|  |   if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   // Make the memory buffer point to the new buffer | ||||||
|  |   if (!RemapMemBuffer(idec, data, data_size)) { | ||||||
|  |     return VP8_STATUS_INVALID_PARAM; | ||||||
|  |   } | ||||||
|  |   return IDecode(idec); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  |  | ||||||
|  | uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int *last_y, | ||||||
|  |                         int* width, int* height, int* stride) { | ||||||
|  |   if (!idec || !idec->dec_ || idec->params_.mode != MODE_RGB || | ||||||
|  |       idec->state_ <= STATE_PARTS0) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (last_y) *last_y = idec->params_.last_y; | ||||||
|  |   if (width) *width = idec->w_; | ||||||
|  |   if (height) *height = idec->h_; | ||||||
|  |   if (stride) *stride = idec->params_.stride; | ||||||
|  |  | ||||||
|  |   return idec->params_.output; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t* WebPIDecGetYUV(const WebPIDecoder* idec, int *last_y, uint8_t** u, | ||||||
|  |                         uint8_t** v, int* width, int* height, int *stride, | ||||||
|  |                         int* uv_stride) { | ||||||
|  |   if (!idec || !idec->dec_ || idec->params_.mode != MODE_YUV || | ||||||
|  |       idec->state_ <= STATE_PARTS0) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (last_y) *last_y = idec->params_.last_y; | ||||||
|  |   if (u) *u = idec->params_.u; | ||||||
|  |   if (v) *v = idec->params_.v; | ||||||
|  |   if (width) *width = idec->w_; | ||||||
|  |   if (height) *height = idec->h_; | ||||||
|  |   if (stride) *stride = idec->params_.stride; | ||||||
|  |   if (uv_stride) *uv_stride = idec->params_.u_stride; | ||||||
|  |  | ||||||
|  |   return idec->params_.output; | ||||||
|  | } | ||||||
| @@ -9,7 +9,6 @@ | |||||||
| // | // | ||||||
| // Author: Skal (pascal.massimino@gmail.com) | // Author: Skal (pascal.massimino@gmail.com) | ||||||
|  |  | ||||||
| #include <stdio.h> |  | ||||||
| #include "vp8i.h" | #include "vp8i.h" | ||||||
|  |  | ||||||
| #define USE_GENERIC_TREE | #define USE_GENERIC_TREE | ||||||
|   | |||||||
							
								
								
									
										112
									
								
								src/dec/webp.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/dec/webp.c
									
									
									
									
									
								
							| @@ -319,12 +319,7 @@ void WebPInitCustomIo(VP8Io* const io) { | |||||||
|  |  | ||||||
| int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, | int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, | ||||||
|                       int* height, WebPDecParams* const params) { |                       int* height, WebPDecParams* const params) { | ||||||
|   int w, h, stride; |   int w, h; | ||||||
|   int uv_size = 0; |  | ||||||
|   int uv_stride = 0; |  | ||||||
|   int size; |  | ||||||
|   uint8_t* output; |  | ||||||
|   WEBP_CSP_MODE mode = params->mode; |  | ||||||
|  |  | ||||||
|   if (!WebPGetInfo(data, data_size, &w, &h)) { |   if (!WebPGetInfo(data, data_size, &w, &h)) { | ||||||
|     return 0; |     return 0; | ||||||
| @@ -332,38 +327,49 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, | |||||||
|   if (width) *width = w; |   if (width) *width = w; | ||||||
|   if (height) *height = h; |   if (height) *height = h; | ||||||
|  |  | ||||||
|   // initialize output buffer, now that dimensions are known. |   if (!params->external_buffer) { | ||||||
|   stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w |     int stride; | ||||||
|       : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w |     int uv_stride = 0; | ||||||
|       : w; |     int size; | ||||||
|   size = stride * h; |     int uv_size = 0; | ||||||
|  |     uint8_t* output; | ||||||
|  |     WEBP_CSP_MODE mode = params->mode; | ||||||
|  |  | ||||||
|   if (mode == MODE_YUV) { |     // initialize output buffer, now that dimensions are known. | ||||||
|     uv_stride = (w + 1) / 2; |     stride = (mode == MODE_RGB || mode == MODE_BGR) ? 3 * w | ||||||
|     uv_size = uv_stride * ((h + 1) / 2); |         : (mode == MODE_RGBA || mode == MODE_BGRA) ? 4 * w | ||||||
|   } |         : w; | ||||||
|  |     size = stride * h; | ||||||
|  |  | ||||||
|   output = (uint8_t*)malloc(size + 2 * uv_size); |     if (mode == MODE_YUV) { | ||||||
|   if (!output) { |       uv_stride = (w + 1) / 2; | ||||||
|     return 0; |       uv_size = uv_stride * ((h + 1) / 2); | ||||||
|   } |     } | ||||||
|  |  | ||||||
|   params->output = output; |     output = (uint8_t*)malloc(size + 2 * uv_size); | ||||||
|   params->stride = stride; |     if (!output) { | ||||||
|   if (mode == MODE_YUV) { |       return 0; | ||||||
|     params->u = output + size; |     } | ||||||
|     params->u_stride = uv_stride; |  | ||||||
|     params->v = output + size + uv_size; |     params->output = output; | ||||||
|     params->v_stride = uv_stride; |     params->stride = stride; | ||||||
|  |     params->output_size = size; | ||||||
|  |     if (mode == MODE_YUV) { | ||||||
|  |       params->u = output + size; | ||||||
|  |       params->u_stride = uv_stride; | ||||||
|  |       params->output_u_size = uv_size; | ||||||
|  |       params->v = output + size + uv_size; | ||||||
|  |       params->v_stride = uv_stride; | ||||||
|  |       params->output_v_size = uv_size; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   return 1; |   return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, | int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params) { | ||||||
|                        int output_size, int output_u_size, int output_v_size) { |  | ||||||
|   int ok = 1; |   int ok = 1; | ||||||
|   WEBP_CSP_MODE mode = params->mode; |   WEBP_CSP_MODE mode = params->mode; | ||||||
|   ok &= (params->stride * io->height <= output_size); |   ok &= (params->stride * io->height <= params->output_size); | ||||||
|   if (mode == MODE_RGB || mode == MODE_BGR) { |   if (mode == MODE_RGB || mode == MODE_BGR) { | ||||||
|     ok &= (params->stride >= io->width * 3); |     ok &= (params->stride >= io->width * 3); | ||||||
|   } else if (mode == MODE_RGBA || mode == MODE_BGRA) { |   } else if (mode == MODE_RGBA || mode == MODE_BGRA) { | ||||||
| @@ -375,13 +381,16 @@ int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, | |||||||
|     ok &= (params->stride >= io->width); |     ok &= (params->stride >= io->width); | ||||||
|     ok &= (params->u_stride >= (io->width + 1) / 2) && |     ok &= (params->u_stride >= (io->width + 1) / 2) && | ||||||
|           (params->v_stride >= (io->width + 1) / 2); |           (params->v_stride >= (io->width + 1) / 2); | ||||||
|     ok &= (u_size <= output_u_size && v_size <= output_v_size); |     ok &= (u_size <= params->output_u_size && | ||||||
|  |            v_size <= params->output_v_size); | ||||||
|   } |   } | ||||||
|   return ok; |   return ok; | ||||||
| } | } | ||||||
|  |  | ||||||
| void WebPClearDecParams(WebPDecParams* params) { | void WebPClearDecParams(WebPDecParams* params) { | ||||||
|   free(params->output); |   if (!params->external_buffer) { | ||||||
|  |     free(params->output); | ||||||
|  |   } | ||||||
|   memset(params, 0, sizeof(*params)); |   memset(params, 0, sizeof(*params)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -390,8 +399,7 @@ void WebPClearDecParams(WebPDecParams* params) { | |||||||
|  |  | ||||||
| static uint8_t* DecodeInto(WEBP_CSP_MODE mode, | static uint8_t* DecodeInto(WEBP_CSP_MODE mode, | ||||||
|                            const uint8_t* data, uint32_t data_size, |                            const uint8_t* data, uint32_t data_size, | ||||||
|                            WebPDecParams* params, int output_size, |                            WebPDecParams* params) { | ||||||
|                            int output_u_size, int output_v_size) { |  | ||||||
|   VP8Decoder* dec = VP8New(); |   VP8Decoder* dec = VP8New(); | ||||||
|   VP8Io io; |   VP8Io io; | ||||||
|   int ok = 1; |   int ok = 1; | ||||||
| @@ -414,8 +422,7 @@ static uint8_t* DecodeInto(WEBP_CSP_MODE mode, | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // check output buffers |   // check output buffers | ||||||
|   ok = WebPCheckDecParams(&io, params, output_size, |   ok = WebPCheckDecParams(&io, params); | ||||||
|                           output_u_size, output_v_size); |  | ||||||
|   if (!ok) { |   if (!ok) { | ||||||
|     VP8Delete(dec); |     VP8Delete(dec); | ||||||
|     return NULL; |     return NULL; | ||||||
| @@ -441,7 +448,10 @@ uint8_t* WebPDecodeRGBInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
|   params.output = output; |   params.output = output; | ||||||
|   params.stride = output_stride; |   params.stride = output_stride; | ||||||
|   return DecodeInto(MODE_RGB, data, data_size, ¶ms, output_size, 0, 0); |   params.output_size = output_size; | ||||||
|  |   params.output_u_size = 0; | ||||||
|  |   params.output_v_size = 0; | ||||||
|  |   return DecodeInto(MODE_RGB, data, data_size, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, | uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, | ||||||
| @@ -455,7 +465,10 @@ uint8_t* WebPDecodeRGBAInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
|   params.output = output; |   params.output = output; | ||||||
|   params.stride = output_stride; |   params.stride = output_stride; | ||||||
|   return DecodeInto(MODE_RGBA, data, data_size, ¶ms, output_size, 0, 0); |   params.output_size = output_size; | ||||||
|  |   params.output_u_size = 0; | ||||||
|  |   params.output_v_size = 0; | ||||||
|  |   return DecodeInto(MODE_RGBA, data, data_size, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, | uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, | ||||||
| @@ -469,7 +482,10 @@ uint8_t* WebPDecodeBGRInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
|   params.output = output; |   params.output = output; | ||||||
|   params.stride = output_stride; |   params.stride = output_stride; | ||||||
|   return DecodeInto(MODE_BGR, data, data_size, ¶ms, output_size, 0, 0); |   params.output_size = output_size; | ||||||
|  |   params.output_u_size = 0; | ||||||
|  |   params.output_v_size = 0; | ||||||
|  |   return DecodeInto(MODE_BGR, data, data_size, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, | uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, | ||||||
| @@ -483,7 +499,10 @@ uint8_t* WebPDecodeBGRAInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
|   params.output = output; |   params.output = output; | ||||||
|   params.stride = output_stride; |   params.stride = output_stride; | ||||||
|   return DecodeInto(MODE_BGRA, data, data_size, ¶ms, output_size, 0, 0); |   params.output_size = output_size; | ||||||
|  |   params.output_u_size = 0; | ||||||
|  |   params.output_v_size = 0; | ||||||
|  |   return DecodeInto(MODE_BGRA, data, data_size, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, | uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, | ||||||
| @@ -498,12 +517,14 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
|   params.output = luma; |   params.output = luma; | ||||||
|   params.stride = luma_stride; |   params.stride = luma_stride; | ||||||
|  |   params.output_size = luma_size; | ||||||
|   params.u = u; |   params.u = u; | ||||||
|   params.u_stride = u_stride; |   params.u_stride = u_stride; | ||||||
|  |   params.output_u_size = u_size; | ||||||
|   params.v = v; |   params.v = v; | ||||||
|   params.v_stride = v_stride; |   params.v_stride = v_stride; | ||||||
|   return DecodeInto(MODE_YUV, data, data_size, ¶ms, |   params.output_v_size = v_size; | ||||||
|                     luma_size, u_size, v_size); |   return DecodeInto(MODE_YUV, data, data_size, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
| @@ -511,8 +532,6 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, | |||||||
| static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, | static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, | ||||||
|                        uint32_t data_size, int* width, int* height, |                        uint32_t data_size, int* width, int* height, | ||||||
|                        WebPDecParams* params_out) { |                        WebPDecParams* params_out) { | ||||||
|   int size = 0; |  | ||||||
|   int uv_size = 0; |  | ||||||
|   uint8_t* output; |   uint8_t* output; | ||||||
|   WebPDecParams params = { 0 }; |   WebPDecParams params = { 0 }; | ||||||
|  |  | ||||||
| @@ -521,9 +540,10 @@ static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* data, | |||||||
|     return NULL; |     return NULL; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   size = params.stride * (*height); |   params.output_size = params.stride * (*height); | ||||||
|   uv_size = params.u_stride * ((*height + 1) / 2); |   params.output_u_size = params.output_v_size = | ||||||
|   output = DecodeInto(mode, data, data_size, ¶ms, size, uv_size, uv_size); |     params.u_stride * ((*height + 1) / 2); | ||||||
|  |   output = DecodeInto(mode, data, data_size, ¶ms); | ||||||
|   if (!output) { |   if (!output) { | ||||||
|     WebPClearDecParams(¶ms); |     WebPClearDecParams(¶ms); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -18,20 +18,20 @@ extern "C" { | |||||||
|  |  | ||||||
| #include "webp/decode_vp8.h" | #include "webp/decode_vp8.h" | ||||||
|  |  | ||||||
| typedef enum { MODE_RGB = 0, MODE_RGBA = 1, | // Decoding output parameters. | ||||||
|                MODE_BGR = 2, MODE_BGRA = 3, |  | ||||||
|                MODE_YUV = 4 } WEBP_CSP_MODE; |  | ||||||
|  |  | ||||||
|   // Decoding output parameters. |  | ||||||
| typedef struct { | typedef struct { | ||||||
|   uint8_t* output;      // rgb(a) or luma |   uint8_t* output;      // rgb(a) or luma | ||||||
|   uint8_t *u, *v; |   uint8_t *u, *v;       // chroma u/v | ||||||
|   uint8_t *top_y, *top_u, *top_v; |   uint8_t *top_y, *top_u, *top_v;   // cache for the fancy upscaler | ||||||
|   int stride;           // rgb(a) stride or luma stride |   int stride;           // rgb(a) stride or luma stride | ||||||
|   int u_stride; |   int u_stride;         // chroma-u stride | ||||||
|   int v_stride; |   int v_stride;         // chroma-v stride | ||||||
|   WEBP_CSP_MODE mode; |   WEBP_CSP_MODE mode;   // rgb(a) or yuv | ||||||
|   int last_y;           // coordinate of the line that was last output |   int last_y;           // coordinate of the line that was last output | ||||||
|  |   int output_size;      // size of 'output' buffer | ||||||
|  |   int output_u_size;    // size of 'u' buffer | ||||||
|  |   int output_v_size;    // size of 'v' buffer | ||||||
|  |   int external_buffer;  // If true, the output buffers are externally owned | ||||||
| } WebPDecParams; | } WebPDecParams; | ||||||
|  |  | ||||||
| // If a RIFF container is detected, validate it and skip over it. Returns | // If a RIFF container is detected, validate it and skip over it. Returns | ||||||
| @@ -50,8 +50,7 @@ int WebPInitDecParams(const uint8_t* data, uint32_t data_size, int* width, | |||||||
|  |  | ||||||
| // Verifies various size configurations (e.g stride >= width, specified | // Verifies various size configurations (e.g stride >= width, specified | ||||||
| // output size <= stride * height etc.). Returns 0 if checks fail. | // output size <= stride * height etc.). Returns 0 if checks fail. | ||||||
| int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params, | int WebPCheckDecParams(const VP8Io* io, const WebPDecParams* params); | ||||||
|                        int output_size, int output_u_size, int output_v_size); |  | ||||||
|  |  | ||||||
| // Deallocate memory allocated by WebPInitDecParams() and reset the | // Deallocate memory allocated by WebPInitDecParams() and reset the | ||||||
| // WebPDecParams object. | // WebPDecParams object. | ||||||
|   | |||||||
| @@ -92,6 +92,100 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, | |||||||
|  |  | ||||||
| //----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | // Output colorspaces | ||||||
|  | typedef enum { MODE_RGB = 0, MODE_RGBA = 1, | ||||||
|  |                MODE_BGR = 2, MODE_BGRA = 3, | ||||||
|  |                MODE_YUV = 4 } WEBP_CSP_MODE; | ||||||
|  |  | ||||||
|  | // Enumeration of the status codes | ||||||
|  | typedef enum { | ||||||
|  |   VP8_STATUS_OK = 0, | ||||||
|  |   VP8_STATUS_OUT_OF_MEMORY, | ||||||
|  |   VP8_STATUS_INVALID_PARAM, | ||||||
|  |   VP8_STATUS_BITSTREAM_ERROR, | ||||||
|  |   VP8_STATUS_UNSUPPORTED_FEATURE, | ||||||
|  |   VP8_STATUS_SUSPENDED, | ||||||
|  |   VP8_STATUS_USER_ABORT, | ||||||
|  |   VP8_STATUS_NOT_ENOUGH_DATA | ||||||
|  | } VP8StatusCode; | ||||||
|  |  | ||||||
|  | //----------------------------------------------------------------------------- | ||||||
|  | // Incremental decoding | ||||||
|  | // | ||||||
|  | //  This API allows streamlined decoding of partial data. | ||||||
|  | //  Picture can be incrementally decoded as data become available thanks to the | ||||||
|  | // WebPIDecoder object. This object can be left in a SUSPENDED state if the | ||||||
|  | // picture is only partially decoded, pending additional input. | ||||||
|  | // Code example: | ||||||
|  | // | ||||||
|  | //   WebPIDecoder* const idec = WebPINew(mode); | ||||||
|  | //   while (has_more_data) { | ||||||
|  | //     // ... (get additional data) | ||||||
|  | //     status = WebPIAppend(idec, new_data, new_data_size); | ||||||
|  | //     if (status != VP8_STATUS_SUSPENDED || | ||||||
|  | //       break; | ||||||
|  | //     } | ||||||
|  | // | ||||||
|  | //     // The above call decodes the current available buffer. | ||||||
|  | //     // Part of the image can now be refreshed by calling to | ||||||
|  | //     // WebPIDecGetRGB()/WebPIDecGetYUV() etc. | ||||||
|  | //   } | ||||||
|  | //   WebPIDelete(idec); | ||||||
|  |  | ||||||
|  | typedef struct WebPIDecoder WebPIDecoder; | ||||||
|  |  | ||||||
|  | // Creates a WebPIDecoder object. Returns NULL in case of failure. | ||||||
|  | WebPIDecoder* WebPINew(WEBP_CSP_MODE mode); | ||||||
|  |  | ||||||
|  | // This function allocates and initializes an incremental-decoder object, which | ||||||
|  | // will output the r/g/b(/a) samples specified by 'mode' into a preallocated | ||||||
|  | // buffer 'output_buffer'. The size of this buffer is at least | ||||||
|  | // 'output_buffer_size' and the stride (distance in bytes between two scanlines) | ||||||
|  | // is specified by 'output_stride'. Returns NULL if the allocation failed. | ||||||
|  | WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, | ||||||
|  |                           int output_buffer_size, int output_stride); | ||||||
|  |  | ||||||
|  | // This function allocates and initializes an incremental-decoder object, which | ||||||
|  | // will output the raw luma/chroma samples into a preallocated planes. The luma | ||||||
|  | // plane is specified by its pointer 'luma', its size 'luma_size' and its stride | ||||||
|  | // 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u', | ||||||
|  | // 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v', 'v_size' | ||||||
|  | // and 'v_size'. | ||||||
|  | // Returns NULL if the allocation failed. | ||||||
|  | WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride, | ||||||
|  |                           uint8_t* u, int u_size, int u_stride, | ||||||
|  |                           uint8_t* v, int v_size, int v_stride); | ||||||
|  |  | ||||||
|  | // Deletes the WebpBuffer object and associated memory. Must always be called | ||||||
|  | // if WebPINew, WebPINewRGB or WebPINewYUV succeeded. | ||||||
|  | void WebPIDelete(WebPIDecoder* const idec); | ||||||
|  |  | ||||||
|  | // Copies and decodes the next available data. Returns VP8_STATUS_OK when | ||||||
|  | // the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more | ||||||
|  | // data is expected. Returns error in other cases. | ||||||
|  | VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data, | ||||||
|  |                           uint32_t data_size); | ||||||
|  |  | ||||||
|  | // A variant of the above function to be used when data buffer contains | ||||||
|  | // partial data from the beginning. In this case data buffer is not copied | ||||||
|  | // to the internal memory. | ||||||
|  | VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data, | ||||||
|  |                           uint32_t data_size); | ||||||
|  |  | ||||||
|  | // Returns the RGB image decoded so far. Returns NULL if output params are not | ||||||
|  | // initialized yet. *last_y is the index of last decoded row in raster scan | ||||||
|  | // order. Some pointers (*last_y, *width etc.) can be NULL if corresponding | ||||||
|  | // information is not needed. | ||||||
|  | uint8_t* WebPIDecGetRGB(const WebPIDecoder* const idec, int *last_y, | ||||||
|  |                         int* width, int* height, int* stride); | ||||||
|  |  | ||||||
|  | // Same as above function to get YUV image. Returns pointer to the luma plane | ||||||
|  | // or NULL in case of error. | ||||||
|  | uint8_t* WebPIDecGetYUV(const WebPIDecoder* const idec, int* last_y, | ||||||
|  |                         uint8_t** u, uint8_t** v, | ||||||
|  |                         int* width, int* height, int* stride, int* uv_stride); | ||||||
|  |  | ||||||
|  |  | ||||||
| #if defined(__cplusplus) || defined(c_plusplus) | #if defined(__cplusplus) || defined(c_plusplus) | ||||||
| }    // extern "C" | }    // extern "C" | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| #ifndef WEBP_WEBP_DECODE_VP8_H_ | #ifndef WEBP_WEBP_DECODE_VP8_H_ | ||||||
| #define WEBP_WEBP_DECODE_VP8_H_ | #define WEBP_WEBP_DECODE_VP8_H_ | ||||||
|  |  | ||||||
| #include "decode.h" | #include "webp/decode.h" | ||||||
|  |  | ||||||
| #if defined(__cplusplus) || defined(c_plusplus) | #if defined(__cplusplus) || defined(c_plusplus) | ||||||
| extern "C" { | extern "C" { | ||||||
| @@ -105,18 +105,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); | |||||||
| // Returns false in case of error. | // Returns false in case of error. | ||||||
| int VP8Decode(VP8Decoder* const dec, VP8Io* const io); | int VP8Decode(VP8Decoder* const dec, VP8Io* const io); | ||||||
|  |  | ||||||
| // Enumeration of the codes returned by VP8Status() |  | ||||||
| typedef enum { |  | ||||||
|   VP8_STATUS_OK = 0, |  | ||||||
|   VP8_STATUS_OUT_OF_MEMORY, |  | ||||||
|   VP8_STATUS_INVALID_PARAM, |  | ||||||
|   VP8_STATUS_BITSTREAM_ERROR, |  | ||||||
|   VP8_STATUS_UNSUPPORTED_FEATURE, |  | ||||||
|   VP8_STATUS_SUSPENDED, |  | ||||||
|   VP8_STATUS_USER_ABORT, |  | ||||||
|   VP8_STATUS_NOT_ENOUGH_DATA, |  | ||||||
| } VP8StatusCode; |  | ||||||
|  |  | ||||||
| // Return current status of the decoder: | // Return current status of the decoder: | ||||||
| VP8StatusCode VP8Status(VP8Decoder* const dec); | VP8StatusCode VP8Status(VP8Decoder* const dec); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user