1 // Copyright 2011 Google Inc. All Rights Reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
10 // Incremental decoding
12 // Author: somnath@google.com (Somnath Banerjee)
21 #include "../utils/utils.h"
23 // In append mode, buffer allocations increase as multiples of this value.
24 // Needs to be a power of 2.
25 #define CHUNK_SIZE 4096
26 #define MAX_MB_SIZE 4096
28 //------------------------------------------------------------------------------
29 // Data structures for memory and states
31 // Decoding states. State normally flows as:
32 // WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
33 // WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
34 // If there is any error the decoder goes into state ERROR.
36 STATE_WEBP_HEADER
, // All the data before that of the VP8/VP8L chunk.
37 STATE_VP8_HEADER
, // The VP8 Frame header (within the VP8 chunk).
46 // Operating state for the MemBuffer
53 // storage for partition #0 and partial data (in a rolling fashion)
55 MemBufferMode mode_
; // Operation mode
56 size_t start_
; // start location of the data to be decoded
57 size_t end_
; // end location
58 size_t buf_size_
; // size of the allocated buffer
59 uint8_t* buf_
; // We don't own this buffer in case WebPIUpdate()
61 size_t part0_size_
; // size of partition #0
62 const uint8_t* part0_buf_
; // buffer to store partition #0
66 DecState state_
; // current decoding state
67 WebPDecParams params_
; // Params to store output info
68 int is_lossless_
; // for down-casting 'dec_'.
69 void* dec_
; // either a VP8Decoder or a VP8LDecoder instance
72 MemBuffer mem_
; // input memory buffer.
73 WebPDecBuffer output_
; // output buffer (when no external one is supplied)
74 size_t chunk_size_
; // Compressed VP8/VP8L size extracted from Header.
76 int last_mb_y_
; // last row reached for intra-mode decoding
79 // MB context to restore in case VP8DecodeMB() fails
83 VP8BitReader token_br_
;
86 //------------------------------------------------------------------------------
87 // MemBuffer: incoming data handling
89 static WEBP_INLINE
size_t MemDataSize(const MemBuffer
* mem
) {
90 return (mem
->end_
- mem
->start_
);
93 // Check if we need to preserve the compressed alpha data, as it may not have
95 static int NeedCompressedAlpha(const WebPIDecoder
* const idec
) {
96 if (idec
->state_
== STATE_WEBP_HEADER
) {
97 // We haven't parsed the headers yet, so we don't know whether the image is
98 // lossy or lossless. This also means that we haven't parsed the ALPH chunk.
101 if (idec
->is_lossless_
) {
102 return 0; // ALPH chunk is not present for lossless images.
104 const VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
105 assert(dec
!= NULL
); // Must be true as idec->state_ != STATE_WEBP_HEADER.
106 return (dec
->alpha_data_
!= NULL
) && !dec
->is_alpha_decoded_
;
110 static void DoRemap(WebPIDecoder
* const idec
, ptrdiff_t offset
) {
111 MemBuffer
* const mem
= &idec
->mem_
;
112 const uint8_t* const new_base
= mem
->buf_
+ mem
->start_
;
113 // note: for VP8, setting up idec->io_ is only really needed at the beginning
114 // of the decoding, till partition #0 is complete.
115 idec
->io_
.data
= new_base
;
116 idec
->io_
.data_size
= MemDataSize(mem
);
118 if (idec
->dec_
!= NULL
) {
119 if (!idec
->is_lossless_
) {
120 VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
121 const int last_part
= dec
->num_parts_
- 1;
124 for (p
= 0; p
<= last_part
; ++p
) {
125 VP8RemapBitReader(dec
->parts_
+ p
, offset
);
127 // Remap partition #0 data pointer to new offset, but only in MAP
128 // mode (in APPEND mode, partition #0 is copied into a fixed memory).
129 if (mem
->mode_
== MEM_MODE_MAP
) {
130 VP8RemapBitReader(&dec
->br_
, offset
);
133 assert(last_part
>= 0);
134 dec
->parts_
[last_part
].buf_end_
= mem
->buf_
+ mem
->end_
;
135 if (NeedCompressedAlpha(idec
)) {
136 ALPHDecoder
* const alph_dec
= dec
->alph_dec_
;
137 dec
->alpha_data_
+= offset
;
138 if (alph_dec
!= NULL
) {
139 if (alph_dec
->method_
== ALPHA_LOSSLESS_COMPRESSION
) {
140 VP8LDecoder
* const alph_vp8l_dec
= alph_dec
->vp8l_dec_
;
141 assert(alph_vp8l_dec
!= NULL
);
142 assert(dec
->alpha_data_size_
>= ALPHA_HEADER_LEN
);
143 VP8LBitReaderSetBuffer(&alph_vp8l_dec
->br_
,
144 dec
->alpha_data_
+ ALPHA_HEADER_LEN
,
145 dec
->alpha_data_size_
- ALPHA_HEADER_LEN
);
146 } else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
147 // Nothing special to do in this case.
151 } else { // Resize lossless bitreader
152 VP8LDecoder
* const dec
= (VP8LDecoder
*)idec
->dec_
;
153 VP8LBitReaderSetBuffer(&dec
->br_
, new_base
, MemDataSize(mem
));
158 // Appends data to the end of MemBuffer->buf_. It expands the allocated memory
159 // size if required and also updates VP8BitReader's if new memory is allocated.
160 static int AppendToMemBuffer(WebPIDecoder
* const idec
,
161 const uint8_t* const data
, size_t data_size
) {
162 VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
163 MemBuffer
* const mem
= &idec
->mem_
;
164 const int need_compressed_alpha
= NeedCompressedAlpha(idec
);
165 const uint8_t* const old_start
= mem
->buf_
+ mem
->start_
;
166 const uint8_t* const old_base
=
167 need_compressed_alpha
? dec
->alpha_data_
: old_start
;
168 assert(mem
->mode_
== MEM_MODE_APPEND
);
169 if (data_size
> MAX_CHUNK_PAYLOAD
) {
170 // security safeguard: trying to allocate more than what the format
171 // allows for a chunk should be considered a smoke smell.
175 if (mem
->end_
+ data_size
> mem
->buf_size_
) { // Need some free memory
176 const size_t new_mem_start
= old_start
- old_base
;
177 const size_t current_size
= MemDataSize(mem
) + new_mem_start
;
178 const uint64_t new_size
= (uint64_t)current_size
+ data_size
;
179 const uint64_t extra_size
= (new_size
+ CHUNK_SIZE
- 1) & ~(CHUNK_SIZE
- 1);
180 uint8_t* const new_buf
=
181 (uint8_t*)WebPSafeMalloc(extra_size
, sizeof(*new_buf
));
182 if (new_buf
== NULL
) return 0;
183 memcpy(new_buf
, old_base
, current_size
);
184 WebPSafeFree(mem
->buf_
);
186 mem
->buf_size_
= (size_t)extra_size
;
187 mem
->start_
= new_mem_start
;
188 mem
->end_
= current_size
;
191 memcpy(mem
->buf_
+ mem
->end_
, data
, data_size
);
192 mem
->end_
+= data_size
;
193 assert(mem
->end_
<= mem
->buf_size_
);
195 DoRemap(idec
, mem
->buf_
+ mem
->start_
- old_start
);
199 static int RemapMemBuffer(WebPIDecoder
* const idec
,
200 const uint8_t* const data
, size_t data_size
) {
201 MemBuffer
* const mem
= &idec
->mem_
;
202 const uint8_t* const old_buf
= mem
->buf_
;
203 const uint8_t* const old_start
= old_buf
+ mem
->start_
;
204 assert(mem
->mode_
== MEM_MODE_MAP
);
206 if (data_size
< mem
->buf_size_
) return 0; // can't remap to a shorter buffer!
208 mem
->buf_
= (uint8_t*)data
;
209 mem
->end_
= mem
->buf_size_
= data_size
;
211 DoRemap(idec
, mem
->buf_
+ mem
->start_
- old_start
);
215 static void InitMemBuffer(MemBuffer
* const mem
) {
216 mem
->mode_
= MEM_MODE_NONE
;
219 mem
->part0_buf_
= NULL
;
220 mem
->part0_size_
= 0;
223 static void ClearMemBuffer(MemBuffer
* const mem
) {
225 if (mem
->mode_
== MEM_MODE_APPEND
) {
226 WebPSafeFree(mem
->buf_
);
227 WebPSafeFree((void*)mem
->part0_buf_
);
231 static int CheckMemBufferMode(MemBuffer
* const mem
, MemBufferMode expected
) {
232 if (mem
->mode_
== MEM_MODE_NONE
) {
233 mem
->mode_
= expected
; // switch to the expected mode
234 } else if (mem
->mode_
!= expected
) {
235 return 0; // we mixed the modes => error
237 assert(mem
->mode_
== expected
); // mode is ok
241 // To be called last.
242 static VP8StatusCode
FinishDecoding(WebPIDecoder
* const idec
) {
243 #if WEBP_DECODER_ABI_VERSION > 0x0203
244 const WebPDecoderOptions
* const options
= idec
->params_
.options
;
245 WebPDecBuffer
* const output
= idec
->params_
.output
;
247 idec
->state_
= STATE_DONE
;
248 if (options
!= NULL
&& options
->flip
) {
249 return WebPFlipBuffer(output
);
252 idec
->state_
= STATE_DONE
;
253 return VP8_STATUS_OK
;
256 //------------------------------------------------------------------------------
257 // Macroblock-decoding contexts
259 static void SaveContext(const VP8Decoder
* dec
, const VP8BitReader
* token_br
,
260 MBContext
* const context
) {
261 context
->left_
= dec
->mb_info_
[-1];
262 context
->info_
= dec
->mb_info_
[dec
->mb_x_
];
263 context
->token_br_
= *token_br
;
266 static void RestoreContext(const MBContext
* context
, VP8Decoder
* const dec
,
267 VP8BitReader
* const token_br
) {
268 dec
->mb_info_
[-1] = context
->left_
;
269 dec
->mb_info_
[dec
->mb_x_
] = context
->info_
;
270 *token_br
= context
->token_br_
;
273 //------------------------------------------------------------------------------
275 static VP8StatusCode
IDecError(WebPIDecoder
* const idec
, VP8StatusCode error
) {
276 if (idec
->state_
== STATE_VP8_DATA
) {
277 VP8Io
* const io
= &idec
->io_
;
278 if (io
->teardown
!= NULL
) {
282 idec
->state_
= STATE_ERROR
;
286 static void ChangeState(WebPIDecoder
* const idec
, DecState new_state
,
287 size_t consumed_bytes
) {
288 MemBuffer
* const mem
= &idec
->mem_
;
289 idec
->state_
= new_state
;
290 mem
->start_
+= consumed_bytes
;
291 assert(mem
->start_
<= mem
->end_
);
292 idec
->io_
.data
= mem
->buf_
+ mem
->start_
;
293 idec
->io_
.data_size
= MemDataSize(mem
);
297 static VP8StatusCode
DecodeWebPHeaders(WebPIDecoder
* const idec
) {
298 MemBuffer
* const mem
= &idec
->mem_
;
299 const uint8_t* data
= mem
->buf_
+ mem
->start_
;
300 size_t curr_size
= MemDataSize(mem
);
301 VP8StatusCode status
;
302 WebPHeaderStructure headers
;
305 headers
.data_size
= curr_size
;
306 headers
.have_all_data
= 0;
307 status
= WebPParseHeaders(&headers
);
308 if (status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
309 return VP8_STATUS_SUSPENDED
; // We haven't found a VP8 chunk yet.
310 } else if (status
!= VP8_STATUS_OK
) {
311 return IDecError(idec
, status
);
314 idec
->chunk_size_
= headers
.compressed_size
;
315 idec
->is_lossless_
= headers
.is_lossless
;
316 if (!idec
->is_lossless_
) {
317 VP8Decoder
* const dec
= VP8New();
319 return VP8_STATUS_OUT_OF_MEMORY
;
322 dec
->alpha_data_
= headers
.alpha_data
;
323 dec
->alpha_data_size_
= headers
.alpha_data_size
;
324 ChangeState(idec
, STATE_VP8_HEADER
, headers
.offset
);
326 VP8LDecoder
* const dec
= VP8LNew();
328 return VP8_STATUS_OUT_OF_MEMORY
;
331 ChangeState(idec
, STATE_VP8L_HEADER
, headers
.offset
);
333 return VP8_STATUS_OK
;
336 static VP8StatusCode
DecodeVP8FrameHeader(WebPIDecoder
* const idec
) {
337 const uint8_t* data
= idec
->mem_
.buf_
+ idec
->mem_
.start_
;
338 const size_t curr_size
= MemDataSize(&idec
->mem_
);
342 if (curr_size
< VP8_FRAME_HEADER_SIZE
) {
343 // Not enough data bytes to extract VP8 Frame Header.
344 return VP8_STATUS_SUSPENDED
;
346 if (!VP8GetInfo(data
, curr_size
, idec
->chunk_size_
, &width
, &height
)) {
347 return IDecError(idec
, VP8_STATUS_BITSTREAM_ERROR
);
350 bits
= data
[0] | (data
[1] << 8) | (data
[2] << 16);
351 idec
->mem_
.part0_size_
= (bits
>> 5) + VP8_FRAME_HEADER_SIZE
;
353 idec
->io_
.data
= data
;
354 idec
->io_
.data_size
= curr_size
;
355 idec
->state_
= STATE_VP8_PARTS0
;
356 return VP8_STATUS_OK
;
360 static int CopyParts0Data(WebPIDecoder
* const idec
) {
361 VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
362 VP8BitReader
* const br
= &dec
->br_
;
363 const size_t psize
= br
->buf_end_
- br
->buf_
;
364 MemBuffer
* const mem
= &idec
->mem_
;
365 assert(!idec
->is_lossless_
);
366 assert(mem
->part0_buf_
== NULL
);
368 assert(psize
<= mem
->part0_size_
); // Format limit: no need for runtime check
369 if (mem
->mode_
== MEM_MODE_APPEND
) {
370 // We copy and grab ownership of the partition #0 data.
371 uint8_t* const part0_buf
= (uint8_t*)WebPSafeMalloc(1ULL, psize
);
372 if (part0_buf
== NULL
) {
375 memcpy(part0_buf
, br
->buf_
, psize
);
376 mem
->part0_buf_
= part0_buf
;
377 br
->buf_
= part0_buf
;
378 br
->buf_end_
= part0_buf
+ psize
;
380 // Else: just keep pointers to the partition #0's data in dec_->br_.
382 mem
->start_
+= psize
;
386 static VP8StatusCode
DecodePartition0(WebPIDecoder
* const idec
) {
387 VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
388 VP8Io
* const io
= &idec
->io_
;
389 const WebPDecParams
* const params
= &idec
->params_
;
390 WebPDecBuffer
* const output
= params
->output
;
392 // Wait till we have enough data for the whole partition #0
393 if (MemDataSize(&idec
->mem_
) < idec
->mem_
.part0_size_
) {
394 return VP8_STATUS_SUSPENDED
;
397 if (!VP8GetHeaders(dec
, io
)) {
398 const VP8StatusCode status
= dec
->status_
;
399 if (status
== VP8_STATUS_SUSPENDED
||
400 status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
401 // treating NOT_ENOUGH_DATA as SUSPENDED state
402 return VP8_STATUS_SUSPENDED
;
404 return IDecError(idec
, status
);
407 // Allocate/Verify output buffer now
408 dec
->status_
= WebPAllocateDecBuffer(io
->width
, io
->height
, params
->options
,
410 if (dec
->status_
!= VP8_STATUS_OK
) {
411 return IDecError(idec
, dec
->status_
);
413 // This change must be done before calling VP8InitFrame()
414 dec
->mt_method_
= VP8GetThreadMethod(params
->options
, NULL
,
415 io
->width
, io
->height
);
416 VP8InitDithering(params
->options
, dec
);
417 if (!CopyParts0Data(idec
)) {
418 return IDecError(idec
, VP8_STATUS_OUT_OF_MEMORY
);
421 // Finish setting up the decoding parameters. Will call io->setup().
422 if (VP8EnterCritical(dec
, io
) != VP8_STATUS_OK
) {
423 return IDecError(idec
, dec
->status_
);
426 // Note: past this point, teardown() must always be called
428 idec
->state_
= STATE_VP8_DATA
;
429 // Allocate memory and prepare everything.
430 if (!VP8InitFrame(dec
, io
)) {
431 return IDecError(idec
, dec
->status_
);
433 return VP8_STATUS_OK
;
436 // Remaining partitions
437 static VP8StatusCode
DecodeRemaining(WebPIDecoder
* const idec
) {
438 VP8Decoder
* const dec
= (VP8Decoder
*)idec
->dec_
;
439 VP8Io
* const io
= &idec
->io_
;
442 for (; dec
->mb_y_
< dec
->mb_h_
; ++dec
->mb_y_
) {
443 if (idec
->last_mb_y_
!= dec
->mb_y_
) {
444 if (!VP8ParseIntraModeRow(&dec
->br_
, dec
)) {
445 // note: normally, error shouldn't occur since we already have the whole
446 // partition0 available here in DecodeRemaining(). Reaching EOF while
447 // reading intra modes really means a BITSTREAM_ERROR.
448 return IDecError(idec
, VP8_STATUS_BITSTREAM_ERROR
);
450 idec
->last_mb_y_
= dec
->mb_y_
;
452 for (; dec
->mb_x_
< dec
->mb_w_
; ++dec
->mb_x_
) {
453 VP8BitReader
* const token_br
=
454 &dec
->parts_
[dec
->mb_y_
& (dec
->num_parts_
- 1)];
456 SaveContext(dec
, token_br
, &context
);
457 if (!VP8DecodeMB(dec
, token_br
)) {
458 // We shouldn't fail when MAX_MB data was available
459 if (dec
->num_parts_
== 1 && MemDataSize(&idec
->mem_
) > MAX_MB_SIZE
) {
460 return IDecError(idec
, VP8_STATUS_BITSTREAM_ERROR
);
462 RestoreContext(&context
, dec
, token_br
);
463 return VP8_STATUS_SUSPENDED
;
465 // Release buffer only if there is only one partition
466 if (dec
->num_parts_
== 1) {
467 idec
->mem_
.start_
= token_br
->buf_
- idec
->mem_
.buf_
;
468 assert(idec
->mem_
.start_
<= idec
->mem_
.end_
);
471 VP8InitScanline(dec
); // Prepare for next scanline
473 // Reconstruct, filter and emit the row.
474 if (!VP8ProcessRow(dec
, io
)) {
475 return IDecError(idec
, VP8_STATUS_USER_ABORT
);
478 // Synchronize the thread and check for errors.
479 if (!VP8ExitCritical(dec
, io
)) {
480 return IDecError(idec
, VP8_STATUS_USER_ABORT
);
483 return FinishDecoding(idec
);
486 static VP8StatusCode
ErrorStatusLossless(WebPIDecoder
* const idec
,
487 VP8StatusCode status
) {
488 if (status
== VP8_STATUS_SUSPENDED
|| status
== VP8_STATUS_NOT_ENOUGH_DATA
) {
489 return VP8_STATUS_SUSPENDED
;
491 return IDecError(idec
, status
);
494 static VP8StatusCode
DecodeVP8LHeader(WebPIDecoder
* const idec
) {
495 VP8Io
* const io
= &idec
->io_
;
496 VP8LDecoder
* const dec
= (VP8LDecoder
*)idec
->dec_
;
497 const WebPDecParams
* const params
= &idec
->params_
;
498 WebPDecBuffer
* const output
= params
->output
;
499 size_t curr_size
= MemDataSize(&idec
->mem_
);
500 assert(idec
->is_lossless_
);
502 // Wait until there's enough data for decoding header.
503 if (curr_size
< (idec
->chunk_size_
>> 3)) {
504 return VP8_STATUS_SUSPENDED
;
506 if (!VP8LDecodeHeader(dec
, io
)) {
507 return ErrorStatusLossless(idec
, dec
->status_
);
509 // Allocate/verify output buffer now.
510 dec
->status_
= WebPAllocateDecBuffer(io
->width
, io
->height
, params
->options
,
512 if (dec
->status_
!= VP8_STATUS_OK
) {
513 return IDecError(idec
, dec
->status_
);
516 idec
->state_
= STATE_VP8L_DATA
;
517 return VP8_STATUS_OK
;
520 static VP8StatusCode
DecodeVP8LData(WebPIDecoder
* const idec
) {
521 VP8LDecoder
* const dec
= (VP8LDecoder
*)idec
->dec_
;
522 const size_t curr_size
= MemDataSize(&idec
->mem_
);
523 assert(idec
->is_lossless_
);
525 // At present Lossless decoder can't decode image incrementally. So wait till
526 // all the image data is aggregated before image can be decoded.
527 if (curr_size
< idec
->chunk_size_
) {
528 return VP8_STATUS_SUSPENDED
;
531 if (!VP8LDecodeImage(dec
)) {
532 return ErrorStatusLossless(idec
, dec
->status_
);
535 return FinishDecoding(idec
);
538 // Main decoding loop
539 static VP8StatusCode
IDecode(WebPIDecoder
* idec
) {
540 VP8StatusCode status
= VP8_STATUS_SUSPENDED
;
542 if (idec
->state_
== STATE_WEBP_HEADER
) {
543 status
= DecodeWebPHeaders(idec
);
545 if (idec
->dec_
== NULL
) {
546 return VP8_STATUS_SUSPENDED
; // can't continue if we have no decoder.
549 if (idec
->state_
== STATE_VP8_HEADER
) {
550 status
= DecodeVP8FrameHeader(idec
);
552 if (idec
->state_
== STATE_VP8_PARTS0
) {
553 status
= DecodePartition0(idec
);
555 if (idec
->state_
== STATE_VP8_DATA
) {
556 status
= DecodeRemaining(idec
);
558 if (idec
->state_
== STATE_VP8L_HEADER
) {
559 status
= DecodeVP8LHeader(idec
);
561 if (idec
->state_
== STATE_VP8L_DATA
) {
562 status
= DecodeVP8LData(idec
);
567 //------------------------------------------------------------------------------
570 WebPIDecoder
* WebPINewDecoder(WebPDecBuffer
* output_buffer
) {
571 WebPIDecoder
* idec
= (WebPIDecoder
*)WebPSafeCalloc(1ULL, sizeof(*idec
));
576 idec
->state_
= STATE_WEBP_HEADER
;
577 idec
->chunk_size_
= 0;
579 idec
->last_mb_y_
= -1;
581 InitMemBuffer(&idec
->mem_
);
582 WebPInitDecBuffer(&idec
->output_
);
583 VP8InitIo(&idec
->io_
);
585 WebPResetDecParams(&idec
->params_
);
586 idec
->params_
.output
= (output_buffer
!= NULL
) ? output_buffer
588 WebPInitCustomIo(&idec
->params_
, &idec
->io_
); // Plug the I/O functions.
593 WebPIDecoder
* WebPIDecode(const uint8_t* data
, size_t data_size
,
594 WebPDecoderConfig
* config
) {
597 // Parse the bitstream's features, if requested:
598 if (data
!= NULL
&& data_size
> 0 && config
!= NULL
) {
599 if (WebPGetFeatures(data
, data_size
, &config
->input
) != VP8_STATUS_OK
) {
603 // Create an instance of the incremental decoder
604 idec
= WebPINewDecoder(config
? &config
->output
: NULL
);
608 // Finish initialization
609 if (config
!= NULL
) {
610 idec
->params_
.options
= &config
->options
;
615 void WebPIDelete(WebPIDecoder
* idec
) {
616 if (idec
== NULL
) return;
617 if (idec
->dec_
!= NULL
) {
618 if (!idec
->is_lossless_
) {
619 if (idec
->state_
== STATE_VP8_DATA
) {
620 // Synchronize the thread, clean-up and check for errors.
621 VP8ExitCritical((VP8Decoder
*)idec
->dec_
, &idec
->io_
);
623 VP8Delete((VP8Decoder
*)idec
->dec_
);
625 VP8LDelete((VP8LDecoder
*)idec
->dec_
);
628 ClearMemBuffer(&idec
->mem_
);
629 WebPFreeDecBuffer(&idec
->output_
);
633 //------------------------------------------------------------------------------
634 // Wrapper toward WebPINewDecoder
636 WebPIDecoder
* WebPINewRGB(WEBP_CSP_MODE mode
, uint8_t* output_buffer
,
637 size_t output_buffer_size
, int output_stride
) {
638 const int is_external_memory
= (output_buffer
!= NULL
);
641 if (mode
>= MODE_YUV
) return NULL
;
642 if (!is_external_memory
) { // Overwrite parameters to sane values.
643 output_buffer_size
= 0;
645 } else { // A buffer was passed. Validate the other params.
646 if (output_stride
== 0 || output_buffer_size
== 0) {
647 return NULL
; // invalid parameter.
650 idec
= WebPINewDecoder(NULL
);
651 if (idec
== NULL
) return NULL
;
652 idec
->output_
.colorspace
= mode
;
653 idec
->output_
.is_external_memory
= is_external_memory
;
654 idec
->output_
.u
.RGBA
.rgba
= output_buffer
;
655 idec
->output_
.u
.RGBA
.stride
= output_stride
;
656 idec
->output_
.u
.RGBA
.size
= output_buffer_size
;
660 WebPIDecoder
* WebPINewYUVA(uint8_t* luma
, size_t luma_size
, int luma_stride
,
661 uint8_t* u
, size_t u_size
, int u_stride
,
662 uint8_t* v
, size_t v_size
, int v_stride
,
663 uint8_t* a
, size_t a_size
, int a_stride
) {
664 const int is_external_memory
= (luma
!= NULL
);
666 WEBP_CSP_MODE colorspace
;
668 if (!is_external_memory
) { // Overwrite parameters to sane values.
669 luma_size
= u_size
= v_size
= a_size
= 0;
670 luma_stride
= u_stride
= v_stride
= a_stride
= 0;
672 colorspace
= MODE_YUVA
;
673 } else { // A luma buffer was passed. Validate the other parameters.
674 if (u
== NULL
|| v
== NULL
) return NULL
;
675 if (luma_size
== 0 || u_size
== 0 || v_size
== 0) return NULL
;
676 if (luma_stride
== 0 || u_stride
== 0 || v_stride
== 0) return NULL
;
678 if (a_size
== 0 || a_stride
== 0) return NULL
;
680 colorspace
= (a
== NULL
) ? MODE_YUV
: MODE_YUVA
;
683 idec
= WebPINewDecoder(NULL
);
684 if (idec
== NULL
) return NULL
;
686 idec
->output_
.colorspace
= colorspace
;
687 idec
->output_
.is_external_memory
= is_external_memory
;
688 idec
->output_
.u
.YUVA
.y
= luma
;
689 idec
->output_
.u
.YUVA
.y_stride
= luma_stride
;
690 idec
->output_
.u
.YUVA
.y_size
= luma_size
;
691 idec
->output_
.u
.YUVA
.u
= u
;
692 idec
->output_
.u
.YUVA
.u_stride
= u_stride
;
693 idec
->output_
.u
.YUVA
.u_size
= u_size
;
694 idec
->output_
.u
.YUVA
.v
= v
;
695 idec
->output_
.u
.YUVA
.v_stride
= v_stride
;
696 idec
->output_
.u
.YUVA
.v_size
= v_size
;
697 idec
->output_
.u
.YUVA
.a
= a
;
698 idec
->output_
.u
.YUVA
.a_stride
= a_stride
;
699 idec
->output_
.u
.YUVA
.a_size
= a_size
;
703 WebPIDecoder
* WebPINewYUV(uint8_t* luma
, size_t luma_size
, int luma_stride
,
704 uint8_t* u
, size_t u_size
, int u_stride
,
705 uint8_t* v
, size_t v_size
, int v_stride
) {
706 return WebPINewYUVA(luma
, luma_size
, luma_stride
,
712 //------------------------------------------------------------------------------
714 static VP8StatusCode
IDecCheckStatus(const WebPIDecoder
* const idec
) {
716 if (idec
->state_
== STATE_ERROR
) {
717 return VP8_STATUS_BITSTREAM_ERROR
;
719 if (idec
->state_
== STATE_DONE
) {
720 return VP8_STATUS_OK
;
722 return VP8_STATUS_SUSPENDED
;
725 VP8StatusCode
WebPIAppend(WebPIDecoder
* idec
,
726 const uint8_t* data
, size_t data_size
) {
727 VP8StatusCode status
;
728 if (idec
== NULL
|| data
== NULL
) {
729 return VP8_STATUS_INVALID_PARAM
;
731 status
= IDecCheckStatus(idec
);
732 if (status
!= VP8_STATUS_SUSPENDED
) {
735 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
736 if (!CheckMemBufferMode(&idec
->mem_
, MEM_MODE_APPEND
)) {
737 return VP8_STATUS_INVALID_PARAM
;
739 // Append data to memory buffer
740 if (!AppendToMemBuffer(idec
, data
, data_size
)) {
741 return VP8_STATUS_OUT_OF_MEMORY
;
743 return IDecode(idec
);
746 VP8StatusCode
WebPIUpdate(WebPIDecoder
* idec
,
747 const uint8_t* data
, size_t data_size
) {
748 VP8StatusCode status
;
749 if (idec
== NULL
|| data
== NULL
) {
750 return VP8_STATUS_INVALID_PARAM
;
752 status
= IDecCheckStatus(idec
);
753 if (status
!= VP8_STATUS_SUSPENDED
) {
756 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
757 if (!CheckMemBufferMode(&idec
->mem_
, MEM_MODE_MAP
)) {
758 return VP8_STATUS_INVALID_PARAM
;
760 // Make the memory buffer point to the new buffer
761 if (!RemapMemBuffer(idec
, data
, data_size
)) {
762 return VP8_STATUS_INVALID_PARAM
;
764 return IDecode(idec
);
767 //------------------------------------------------------------------------------
769 static const WebPDecBuffer
* GetOutputBuffer(const WebPIDecoder
* const idec
) {
770 if (idec
== NULL
|| idec
->dec_
== NULL
) {
773 if (idec
->state_
<= STATE_VP8_PARTS0
) {
776 return idec
->params_
.output
;
779 const WebPDecBuffer
* WebPIDecodedArea(const WebPIDecoder
* idec
,
781 int* width
, int* height
) {
782 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
783 if (left
!= NULL
) *left
= 0;
784 if (top
!= NULL
) *top
= 0;
785 // TODO(skal): later include handling of rotations.
787 if (width
!= NULL
) *width
= src
->width
;
788 if (height
!= NULL
) *height
= idec
->params_
.last_y
;
790 if (width
!= NULL
) *width
= 0;
791 if (height
!= NULL
) *height
= 0;
796 uint8_t* WebPIDecGetRGB(const WebPIDecoder
* idec
, int* last_y
,
797 int* width
, int* height
, int* stride
) {
798 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
799 if (src
== NULL
) return NULL
;
800 if (src
->colorspace
>= MODE_YUV
) {
804 if (last_y
!= NULL
) *last_y
= idec
->params_
.last_y
;
805 if (width
!= NULL
) *width
= src
->width
;
806 if (height
!= NULL
) *height
= src
->height
;
807 if (stride
!= NULL
) *stride
= src
->u
.RGBA
.stride
;
809 return src
->u
.RGBA
.rgba
;
812 uint8_t* WebPIDecGetYUVA(const WebPIDecoder
* idec
, int* last_y
,
813 uint8_t** u
, uint8_t** v
, uint8_t** a
,
814 int* width
, int* height
,
815 int* stride
, int* uv_stride
, int* a_stride
) {
816 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
817 if (src
== NULL
) return NULL
;
818 if (src
->colorspace
< MODE_YUV
) {
822 if (last_y
!= NULL
) *last_y
= idec
->params_
.last_y
;
823 if (u
!= NULL
) *u
= src
->u
.YUVA
.u
;
824 if (v
!= NULL
) *v
= src
->u
.YUVA
.v
;
825 if (a
!= NULL
) *a
= src
->u
.YUVA
.a
;
826 if (width
!= NULL
) *width
= src
->width
;
827 if (height
!= NULL
) *height
= src
->height
;
828 if (stride
!= NULL
) *stride
= src
->u
.YUVA
.y_stride
;
829 if (uv_stride
!= NULL
) *uv_stride
= src
->u
.YUVA
.u_stride
;
830 if (a_stride
!= NULL
) *a_stride
= src
->u
.YUVA
.a_stride
;
832 return src
->u
.YUVA
.y
;
835 int WebPISetIOHooks(WebPIDecoder
* const idec
,
837 VP8IoSetupHook setup
,
838 VP8IoTeardownHook teardown
,
840 if (idec
== NULL
|| idec
->state_
> STATE_WEBP_HEADER
) {
845 idec
->io_
.setup
= setup
;
846 idec
->io_
.teardown
= teardown
;
847 idec
->io_
.opaque
= user_data
;