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