• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 Google Inc.
2 //
3 // This code is licensed under the same terms as WebM:
4 //  Software License Agreement:  http://www.webmproject.org/license/software/
5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // Incremental decoding
9 //
10 // Author: somnath@google.com (Somnath Banerjee)
11 
12 #include <assert.h>
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #include "webpi.h"
17 #include "vp8i.h"
18 
19 #if defined(__cplusplus) || defined(c_plusplus)
20 extern "C" {
21 #endif
22 
23 #define RIFF_HEADER_SIZE 20
24 #define VP8_HEADER_SIZE 10
25 #define WEBP_HEADER_SIZE (RIFF_HEADER_SIZE + VP8_HEADER_SIZE)
26 #define CHUNK_SIZE 4096
27 #define MAX_MB_SIZE 4096
28 
29 //------------------------------------------------------------------------------
30 // Data structures for memory and states
31 
32 // Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
33 // If there is any error the decoder goes into state ERROR.
34 typedef enum { STATE_HEADER = 0, STATE_PARTS0 = 1,
35                STATE_DATA = 2, STATE_DONE = 3,
36                STATE_ERROR = 4
37 } DecState;
38 
39 // Operating state for the MemBuffer
40 typedef enum { MEM_MODE_NONE = 0,
41                MEM_MODE_APPEND, MEM_MODE_MAP
42 } MemBufferMode;
43 
44 // storage for partition #0 and partial data (in a rolling fashion)
45 typedef struct {
46   MemBufferMode mode_;  // Operation mode
47   uint32_t start_;      // start location of the data to be decoded
48   uint32_t end_;        // end location
49   size_t buf_size_;     // size of the allocated buffer
50   uint8_t* buf_;        // We don't own this buffer in case WebPIUpdate()
51 
52   size_t part0_size_;         // size of partition #0
53   const uint8_t* part0_buf_;  // buffer to store partition #0
54 } MemBuffer;
55 
56 struct WebPIDecoder {
57   DecState state_;         // current decoding state
58   WebPDecParams params_;   // Params to store output info
59   VP8Decoder* dec_;
60   VP8Io io_;
61 
62   MemBuffer mem_;          // input memory buffer.
63   WebPDecBuffer output_;   // output buffer (when no external one is supplied)
64 };
65 
66 // MB context to restore in case VP8DecodeMB() fails
67 typedef struct {
68   VP8MB left_;
69   VP8MB info_;
70   uint8_t intra_t_[4];
71   uint8_t intra_l_[4];
72   VP8BitReader br_;
73   VP8BitReader token_br_;
74 } MBContext;
75 
76 //------------------------------------------------------------------------------
77 // MemBuffer: incoming data handling
78 
79 #define REMAP(PTR, OLD_BASE, NEW_BASE) (PTR) = (NEW_BASE) + ((PTR) - OLD_BASE)
80 
MemDataSize(const MemBuffer * mem)81 static inline size_t MemDataSize(const MemBuffer* mem) {
82   return (mem->end_ - mem->start_);
83 }
84 
85 // Appends data to the end of MemBuffer->buf_. It expands the allocated memory
86 // size if required and also updates VP8BitReader's if new memory is allocated.
AppendToMemBuffer(WebPIDecoder * const idec,const uint8_t * const data,size_t data_size)87 static int AppendToMemBuffer(WebPIDecoder* const idec,
88                              const uint8_t* const data, size_t data_size) {
89   MemBuffer* const mem = &idec->mem_;
90   VP8Decoder* const dec = idec->dec_;
91   const int last_part = dec->num_parts_ - 1;
92   assert(mem->mode_ == MEM_MODE_APPEND);
93 
94   if (mem->end_ + data_size > mem->buf_size_) {  // Need some free memory
95     int p;
96     uint8_t* new_buf = NULL;
97     const int num_chunks = (MemDataSize(mem) + data_size + CHUNK_SIZE - 1)
98         / CHUNK_SIZE;
99     const size_t new_size = num_chunks * CHUNK_SIZE;
100     const uint8_t* const base = mem->buf_ + mem->start_;
101 
102     new_buf = (uint8_t*)malloc(new_size);
103     if (!new_buf) return 0;
104     memcpy(new_buf, base, MemDataSize(mem));
105 
106     // adjust VP8BitReader pointers
107     for (p = 0; p <= last_part; ++p) {
108       if (dec->parts_[p].buf_) {
109         REMAP(dec->parts_[p].buf_, base, new_buf);
110         REMAP(dec->parts_[p].buf_end_, base, new_buf);
111       }
112     }
113 
114     // adjust memory pointers
115     free(mem->buf_);
116     mem->buf_ = new_buf;
117     mem->buf_size_ = new_size;
118 
119     mem->end_ = MemDataSize(mem);
120     mem->start_ = 0;
121   }
122 
123   memcpy(mem->buf_ + mem->end_, data, data_size);
124   mem->end_ += data_size;
125   assert(mem->end_ <= mem->buf_size_);
126   dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
127 
128   // note: setting up idec->io_ is only really needed at the beginning
129   // of the decoding, till partition #0 is complete.
130   idec->io_.data = mem->buf_ + mem->start_;
131   idec->io_.data_size = MemDataSize(mem);
132   return 1;
133 }
134 
RemapMemBuffer(WebPIDecoder * const idec,const uint8_t * const data,size_t data_size)135 static int RemapMemBuffer(WebPIDecoder* const idec,
136                           const uint8_t* const data, size_t data_size) {
137   int p;
138   MemBuffer* const mem = &idec->mem_;
139   VP8Decoder* const dec = idec->dec_;
140   const int last_part = dec->num_parts_ - 1;
141   const uint8_t* base = mem->buf_;
142 
143   assert(mem->mode_ == MEM_MODE_MAP);
144   if (data_size < mem->buf_size_) {
145     return 0;  // we cannot remap to a shorter buffer!
146   }
147 
148   for (p = 0; p <= last_part; ++p) {
149     if (dec->parts_[p].buf_) {
150       REMAP(dec->parts_[p].buf_, base, data);
151       REMAP(dec->parts_[p].buf_end_, base, data);
152     }
153   }
154   dec->parts_[last_part].buf_end_ = data + data_size;
155 
156   // Remap partition #0 data pointer to new offset.
157   if (dec->br_.buf_) {
158     REMAP(dec->br_.buf_, base, data);
159     REMAP(dec->br_.buf_end_, base, data);
160   }
161 
162   mem->buf_ = (uint8_t*)data;
163   mem->end_ = mem->buf_size_ = data_size;
164 
165   idec->io_.data = data;
166   idec->io_.data_size = data_size;
167   return 1;
168 }
169 
InitMemBuffer(MemBuffer * const mem)170 static void InitMemBuffer(MemBuffer* const mem) {
171   mem->mode_       = MEM_MODE_NONE;
172   mem->buf_        = 0;
173   mem->buf_size_   = 0;
174   mem->part0_buf_  = 0;
175   mem->part0_size_ = 0;
176 }
177 
ClearMemBuffer(MemBuffer * const mem)178 static void ClearMemBuffer(MemBuffer* const mem) {
179   assert(mem);
180   if (mem->mode_ == MEM_MODE_APPEND) {
181     free(mem->buf_);
182     free((void*)mem->part0_buf_);
183   }
184 }
185 
CheckMemBufferMode(MemBuffer * const mem,MemBufferMode expected)186 static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
187   if (mem->mode_ == MEM_MODE_NONE) {
188     mem->mode_ = expected;    // switch to the expected mode
189   } else if (mem->mode_ != expected) {
190     return 0;         // we mixed the modes => error
191   }
192   assert(mem->mode_ == expected);   // mode is ok
193   return 1;
194 }
195 
196 #undef REMAP
197 
198 //------------------------------------------------------------------------------
199 // Macroblock-decoding contexts
200 
SaveContext(const VP8Decoder * dec,const VP8BitReader * token_br,MBContext * const context)201 static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
202                         MBContext* const context) {
203   const VP8BitReader* const br = &dec->br_;
204   const VP8MB* const left = dec->mb_info_ - 1;
205   const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
206 
207   context->left_ = *left;
208   context->info_ = *info;
209   context->br_ = *br;
210   context->token_br_ = *token_br;
211   memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
212   memcpy(context->intra_l_, dec->intra_l_, 4);
213 }
214 
RestoreContext(const MBContext * context,VP8Decoder * const dec,VP8BitReader * const token_br)215 static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
216                            VP8BitReader* const token_br) {
217   VP8BitReader* const br = &dec->br_;
218   VP8MB* const left = dec->mb_info_ - 1;
219   VP8MB* const info = dec->mb_info_ + dec->mb_x_;
220 
221   *left = context->left_;
222   *info = context->info_;
223   *br = context->br_;
224   *token_br = context->token_br_;
225   memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
226   memcpy(dec->intra_l_, context->intra_l_, 4);
227 }
228 
229 //------------------------------------------------------------------------------
230 
IDecError(WebPIDecoder * idec,VP8StatusCode error)231 static VP8StatusCode IDecError(WebPIDecoder* idec, VP8StatusCode error) {
232   if (idec->state_ == STATE_DATA) {
233     VP8Io* const io = &idec->io_;
234     if (io->teardown) {
235       io->teardown(io);
236     }
237   }
238   idec->state_ = STATE_ERROR;
239   return error;
240 }
241 
242 // Header
DecodeHeader(WebPIDecoder * const idec)243 static VP8StatusCode DecodeHeader(WebPIDecoder* const idec) {
244   uint32_t riff_header_size, bits;
245   const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
246   uint32_t curr_size = MemDataSize(&idec->mem_);
247   uint32_t chunk_size;
248 
249   if (curr_size < WEBP_HEADER_SIZE) {
250     return VP8_STATUS_SUSPENDED;
251   }
252 
253   // Validate and Skip over RIFF header
254   chunk_size = WebPCheckRIFFHeader(&data, &curr_size);
255   if (chunk_size == 0 ||
256       curr_size < VP8_HEADER_SIZE ||
257       !VP8GetInfo(data, curr_size, chunk_size, NULL, NULL, NULL)) {
258     return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
259   }
260 
261   riff_header_size = idec->mem_.end_ - curr_size;
262   bits = data[0] | (data[1] << 8) | (data[2] << 16);
263 
264   idec->mem_.part0_size_ = (bits >> 5) + VP8_HEADER_SIZE;
265   idec->mem_.start_ += riff_header_size;
266   assert(idec->mem_.start_ <= idec->mem_.end_);
267 
268   idec->io_.data_size -= riff_header_size;
269   idec->io_.data = data;
270   idec->state_ = STATE_PARTS0;
271   return VP8_STATUS_OK;
272 }
273 
274 // Partition #0
CopyParts0Data(WebPIDecoder * idec)275 static int CopyParts0Data(WebPIDecoder* idec) {
276   VP8BitReader* const br = &idec->dec_->br_;
277   const size_t psize = br->buf_end_ - br->buf_;
278   MemBuffer* const mem = &idec->mem_;
279   assert(!mem->part0_buf_);
280   assert(psize > 0);
281   assert(psize <= mem->part0_size_);
282   if (mem->mode_ == MEM_MODE_APPEND) {
283     // We copy and grab ownership of the partition #0 data.
284     uint8_t* const part0_buf = (uint8_t*)malloc(psize);
285     if (!part0_buf) {
286       return 0;
287     }
288     memcpy(part0_buf, br->buf_, psize);
289     mem->part0_buf_ = part0_buf;
290     mem->start_ += psize;
291     br->buf_ = part0_buf;
292     br->buf_end_ = part0_buf + psize;
293   } else {
294     // Else: just keep pointers to the partition #0's data in dec_->br_.
295   }
296   return 1;
297 }
298 
DecodePartition0(WebPIDecoder * const idec)299 static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
300   VP8Decoder* const dec = idec->dec_;
301   VP8Io* const io = &idec->io_;
302   const WebPDecParams* const params = &idec->params_;
303   WebPDecBuffer* const output = params->output;
304 
305   // Wait till we have enough data for the whole partition #0
306   if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
307     return VP8_STATUS_SUSPENDED;
308   }
309 
310   if (!VP8GetHeaders(dec, io)) {
311     const VP8StatusCode status = dec->status_;
312     if (status == VP8_STATUS_SUSPENDED ||
313         status == VP8_STATUS_NOT_ENOUGH_DATA) {
314       // treating NOT_ENOUGH_DATA as SUSPENDED state
315       return VP8_STATUS_SUSPENDED;
316     }
317     return IDecError(idec, status);
318   }
319 
320   // Allocate/Verify output buffer now
321   dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
322                                        output);
323   if (dec->status_ != VP8_STATUS_OK) {
324     return IDecError(idec, dec->status_);
325   }
326 
327   // Allocate memory and prepare everything.
328   if (!VP8InitFrame(dec, io)) {
329     return IDecError(idec, dec->status_);
330   }
331 
332   if (!CopyParts0Data(idec)) {
333     return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
334   }
335 
336   // Finish setting up the decoding parameters.
337   if (VP8FinishFrameSetup(dec, io) != VP8_STATUS_OK) {
338     return IDecError(idec, dec->status_);
339   }
340   // Note: past this point, teardown() must always be called
341   // in case of error.
342   idec->state_ = STATE_DATA;
343   return VP8_STATUS_OK;
344 }
345 
346 // Remaining partitions
DecodeRemaining(WebPIDecoder * const idec)347 static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
348   VP8BitReader*  br;
349   VP8Decoder* const dec = idec->dec_;
350   VP8Io* const io = &idec->io_;
351 
352   assert(dec->ready_);
353 
354   br = &dec->br_;
355   for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
356     VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
357     if (dec->mb_x_ == 0) {
358       VP8MB* const left = dec->mb_info_ - 1;
359       left->nz_ = 0;
360       left->dc_nz_ = 0;
361       memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
362     }
363 
364     for (; dec->mb_x_ < dec->mb_w_;  dec->mb_x_++) {
365       MBContext context;
366       SaveContext(dec, token_br, &context);
367 
368       if (!VP8DecodeMB(dec, token_br)) {
369         RestoreContext(&context, dec, token_br);
370         // We shouldn't fail when MAX_MB data was available
371         if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
372           return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
373         }
374         return VP8_STATUS_SUSPENDED;
375       }
376       VP8ReconstructBlock(dec);
377       // Store data and save block's filtering params
378       VP8StoreBlock(dec);
379 
380       // Release buffer only if there is only one partition
381       if (dec->num_parts_ == 1) {
382         idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
383         assert(idec->mem_.start_ <= idec->mem_.end_);
384       }
385     }
386     if (dec->filter_type_ > 0) {
387       VP8FilterRow(dec);
388     }
389     if (!VP8FinishRow(dec, io)) {
390       return IDecError(idec, VP8_STATUS_USER_ABORT);
391     }
392     dec->mb_x_ = 0;
393   }
394 
395   if (io->teardown) {
396     io->teardown(io);
397   }
398   dec->ready_ = 0;
399   idec->state_ = STATE_DONE;
400 
401   return VP8_STATUS_OK;
402 }
403 
404   // Main decoding loop
IDecode(WebPIDecoder * idec)405 static VP8StatusCode IDecode(WebPIDecoder* idec) {
406   VP8StatusCode status = VP8_STATUS_SUSPENDED;
407   assert(idec->dec_);
408 
409   if (idec->state_ == STATE_HEADER) {
410     status = DecodeHeader(idec);
411   }
412   if (idec->state_ == STATE_PARTS0) {
413     status = DecodePartition0(idec);
414   }
415   if (idec->state_ == STATE_DATA) {
416     status = DecodeRemaining(idec);
417   }
418   return status;
419 }
420 
421 //------------------------------------------------------------------------------
422 // Public functions
423 
WebPINewDecoder(WebPDecBuffer * const output_buffer)424 WebPIDecoder* WebPINewDecoder(WebPDecBuffer* const output_buffer) {
425   WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(WebPIDecoder));
426   if (idec == NULL) {
427     return NULL;
428   }
429 
430   idec->dec_ = VP8New();
431   if (idec->dec_ == NULL) {
432     free(idec);
433     return NULL;
434   }
435 
436   idec->state_ = STATE_HEADER;
437 
438   InitMemBuffer(&idec->mem_);
439   WebPInitDecBuffer(&idec->output_);
440   VP8InitIo(&idec->io_);
441 
442   WebPResetDecParams(&idec->params_);
443   idec->params_.output = output_buffer ? output_buffer : &idec->output_;
444   WebPInitCustomIo(&idec->params_, &idec->io_);  // Plug the I/O functions.
445 
446   return idec;
447 }
448 
WebPIDecode(const uint8_t * data,uint32_t data_size,WebPDecoderConfig * const config)449 WebPIDecoder* WebPIDecode(const uint8_t* data, uint32_t data_size,
450                           WebPDecoderConfig* const config) {
451   WebPIDecoder* idec;
452 
453   // Parse the bitstream's features, if requested:
454   if (data != NULL && data_size > 0 && config != NULL) {
455     if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
456       return NULL;
457     }
458   }
459   // Create an instance of the incremental decoder
460   idec = WebPINewDecoder(config ? &config->output : NULL);
461   if (!idec) {
462     return NULL;
463   }
464   // Finish initialization
465   if (config != NULL) {
466     idec->params_.options = &config->options;
467   }
468   return idec;
469 }
470 
WebPIDelete(WebPIDecoder * const idec)471 void WebPIDelete(WebPIDecoder* const idec) {
472   if (!idec) return;
473   VP8Delete(idec->dec_);
474   ClearMemBuffer(&idec->mem_);
475   WebPFreeDecBuffer(&idec->output_);
476   free(idec);
477 }
478 
479 //------------------------------------------------------------------------------
480 // Wrapper toward WebPINewDecoder
481 
WebPINew(WEBP_CSP_MODE mode)482 WebPIDecoder* WebPINew(WEBP_CSP_MODE mode) {
483   WebPIDecoder* const idec = WebPINewDecoder(NULL);
484   if (!idec) return NULL;
485   idec->output_.colorspace = mode;
486   return idec;
487 }
488 
WebPINewRGB(WEBP_CSP_MODE mode,uint8_t * output_buffer,int output_buffer_size,int output_stride)489 WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
490                           int output_buffer_size, int output_stride) {
491   WebPIDecoder* idec;
492   if (mode >= MODE_YUV) return NULL;
493   idec = WebPINewDecoder(NULL);
494   if (!idec) return NULL;
495   idec->output_.colorspace = mode;
496   idec->output_.is_external_memory = 1;
497   idec->output_.u.RGBA.rgba = output_buffer;
498   idec->output_.u.RGBA.stride = output_stride;
499   idec->output_.u.RGBA.size = output_buffer_size;
500   return idec;
501 }
502 
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)503 WebPIDecoder* WebPINewYUV(uint8_t* luma, int luma_size, int luma_stride,
504                           uint8_t* u, int u_size, int u_stride,
505                           uint8_t* v, int v_size, int v_stride) {
506   WebPIDecoder* const idec = WebPINewDecoder(NULL);
507   if (!idec) return NULL;
508   idec->output_.colorspace = MODE_YUV;
509   idec->output_.is_external_memory = 1;
510   idec->output_.u.YUVA.y = luma;
511   idec->output_.u.YUVA.y_stride = luma_stride;
512   idec->output_.u.YUVA.y_size = luma_size;
513   idec->output_.u.YUVA.u = u;
514   idec->output_.u.YUVA.u_stride = u_stride;
515   idec->output_.u.YUVA.u_size = u_size;
516   idec->output_.u.YUVA.v = v;
517   idec->output_.u.YUVA.v_stride = v_stride;
518   idec->output_.u.YUVA.v_size = v_size;
519   return idec;
520 }
521 
522 //------------------------------------------------------------------------------
523 
IDecCheckStatus(const WebPIDecoder * const idec)524 static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
525   assert(idec);
526   if (idec->dec_ == NULL) {
527     return VP8_STATUS_USER_ABORT;
528   }
529   if (idec->state_ == STATE_ERROR) {
530     return VP8_STATUS_BITSTREAM_ERROR;
531   }
532   if (idec->state_ == STATE_DONE) {
533     return VP8_STATUS_OK;
534   }
535   return VP8_STATUS_SUSPENDED;
536 }
537 
WebPIAppend(WebPIDecoder * const idec,const uint8_t * data,uint32_t data_size)538 VP8StatusCode WebPIAppend(WebPIDecoder* const idec, const uint8_t* data,
539                           uint32_t data_size) {
540   VP8StatusCode status;
541   if (idec == NULL || data == NULL) {
542     return VP8_STATUS_INVALID_PARAM;
543   }
544   status = IDecCheckStatus(idec);
545   if (status != VP8_STATUS_SUSPENDED) {
546     return status;
547   }
548   // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
549   if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
550     return VP8_STATUS_INVALID_PARAM;
551   }
552   // Append data to memory buffer
553   if (!AppendToMemBuffer(idec, data, data_size)) {
554     return VP8_STATUS_OUT_OF_MEMORY;
555   }
556   return IDecode(idec);
557 }
558 
WebPIUpdate(WebPIDecoder * const idec,const uint8_t * data,uint32_t data_size)559 VP8StatusCode WebPIUpdate(WebPIDecoder* const idec, const uint8_t* data,
560                           uint32_t data_size) {
561   VP8StatusCode status;
562   if (idec == NULL || data == NULL) {
563     return VP8_STATUS_INVALID_PARAM;
564   }
565   status = IDecCheckStatus(idec);
566   if (status != VP8_STATUS_SUSPENDED) {
567     return status;
568   }
569   // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
570   if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
571     return VP8_STATUS_INVALID_PARAM;
572   }
573   // Make the memory buffer point to the new buffer
574   if (!RemapMemBuffer(idec, data, data_size)) {
575     return VP8_STATUS_INVALID_PARAM;
576   }
577   return IDecode(idec);
578 }
579 
580 //------------------------------------------------------------------------------
581 
GetOutputBuffer(const WebPIDecoder * const idec)582 static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
583   if (!idec || !idec->dec_ || idec->state_ <= STATE_PARTS0) {
584     return NULL;
585   }
586   return idec->params_.output;
587 }
588 
WebPIDecodedArea(const WebPIDecoder * const idec,int * const left,int * const top,int * const width,int * const height)589 const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* const idec,
590                                       int* const left, int* const top,
591                                       int* const width, int* const height) {
592   const WebPDecBuffer* const src = GetOutputBuffer(idec);
593   if (left) *left = 0;
594   if (top) *top = 0;
595   // TODO(skal): later include handling of rotations.
596   if (src) {
597     if (width) *width = src->width;
598     if (height) *height = idec->params_.last_y;
599   } else {
600     if (width) *width = 0;
601     if (height) *height = 0;
602   }
603   return src;
604 }
605 
WebPIDecGetRGB(const WebPIDecoder * const idec,int * last_y,int * width,int * height,int * stride)606 uint8_t* WebPIDecGetRGB(const WebPIDecoder* const idec, int* last_y,
607                         int* width, int* height, int* stride) {
608   const WebPDecBuffer* const src = GetOutputBuffer(idec);
609   if (!src) return NULL;
610   if (src->colorspace >= MODE_YUV) {
611     return NULL;
612   }
613 
614   if (last_y) *last_y = idec->params_.last_y;
615   if (width) *width = src->width;
616   if (height) *height = src->height;
617   if (stride) *stride = src->u.RGBA.stride;
618 
619   return src->u.RGBA.rgba;
620 }
621 
WebPIDecGetYUV(const WebPIDecoder * const idec,int * last_y,uint8_t ** u,uint8_t ** v,int * width,int * height,int * stride,int * uv_stride)622 uint8_t* WebPIDecGetYUV(const WebPIDecoder* const idec, int* last_y,
623                         uint8_t** u, uint8_t** v,
624                         int* width, int* height, int *stride, int* uv_stride) {
625   const WebPDecBuffer* const src = GetOutputBuffer(idec);
626   if (!src) return NULL;
627   if (src->colorspace < MODE_YUV) {
628     return NULL;
629   }
630 
631   if (last_y) *last_y = idec->params_.last_y;
632   if (u) *u = src->u.YUVA.u;
633   if (v) *v = src->u.YUVA.v;
634   if (width) *width = src->width;
635   if (height) *height = src->height;
636   if (stride) *stride = src->u.YUVA.y_stride;
637   if (uv_stride) *uv_stride = src->u.YUVA.u_stride;
638 
639   return src->u.YUVA.y;
640 }
641 
WebPISetIOHooks(WebPIDecoder * const idec,VP8IoPutHook put,VP8IoSetupHook setup,VP8IoTeardownHook teardown,void * user_data)642 int WebPISetIOHooks(WebPIDecoder* const idec,
643                     VP8IoPutHook put,
644                     VP8IoSetupHook setup,
645                     VP8IoTeardownHook teardown,
646                     void* user_data) {
647   if (!idec || !idec->dec_ || idec->state_ > STATE_HEADER) {
648     return 0;
649   }
650 
651   idec->io_.put = put;
652   idec->io_.setup = setup;
653   idec->io_.teardown = teardown;
654   idec->io_.opaque = user_data;
655 
656   return 1;
657 }
658 
659 #if defined(__cplusplus) || defined(c_plusplus)
660 }    // extern "C"
661 #endif
662