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 // The decoding is called after all the data-bytes are aggregated. Change
533 // the error to VP8_BITSTREAM_ERROR in case lossless decoder fails to decode
534 // all the pixels (VP8_STATUS_SUSPENDED).
535 if (dec
->status_
== VP8_STATUS_SUSPENDED
) {
536 dec
->status_
= VP8_STATUS_BITSTREAM_ERROR
;
538 return ErrorStatusLossless(idec
, dec
->status_
);
541 return FinishDecoding(idec
);
544 // Main decoding loop
545 static VP8StatusCode
IDecode(WebPIDecoder
* idec
) {
546 VP8StatusCode status
= VP8_STATUS_SUSPENDED
;
548 if (idec
->state_
== STATE_WEBP_HEADER
) {
549 status
= DecodeWebPHeaders(idec
);
551 if (idec
->dec_
== NULL
) {
552 return VP8_STATUS_SUSPENDED
; // can't continue if we have no decoder.
555 if (idec
->state_
== STATE_VP8_HEADER
) {
556 status
= DecodeVP8FrameHeader(idec
);
558 if (idec
->state_
== STATE_VP8_PARTS0
) {
559 status
= DecodePartition0(idec
);
561 if (idec
->state_
== STATE_VP8_DATA
) {
562 status
= DecodeRemaining(idec
);
564 if (idec
->state_
== STATE_VP8L_HEADER
) {
565 status
= DecodeVP8LHeader(idec
);
567 if (idec
->state_
== STATE_VP8L_DATA
) {
568 status
= DecodeVP8LData(idec
);
573 //------------------------------------------------------------------------------
576 WebPIDecoder
* WebPINewDecoder(WebPDecBuffer
* output_buffer
) {
577 WebPIDecoder
* idec
= (WebPIDecoder
*)WebPSafeCalloc(1ULL, sizeof(*idec
));
582 idec
->state_
= STATE_WEBP_HEADER
;
583 idec
->chunk_size_
= 0;
585 idec
->last_mb_y_
= -1;
587 InitMemBuffer(&idec
->mem_
);
588 WebPInitDecBuffer(&idec
->output_
);
589 VP8InitIo(&idec
->io_
);
591 WebPResetDecParams(&idec
->params_
);
592 idec
->params_
.output
= (output_buffer
!= NULL
) ? output_buffer
594 WebPInitCustomIo(&idec
->params_
, &idec
->io_
); // Plug the I/O functions.
599 WebPIDecoder
* WebPIDecode(const uint8_t* data
, size_t data_size
,
600 WebPDecoderConfig
* config
) {
603 // Parse the bitstream's features, if requested:
604 if (data
!= NULL
&& data_size
> 0 && config
!= NULL
) {
605 if (WebPGetFeatures(data
, data_size
, &config
->input
) != VP8_STATUS_OK
) {
609 // Create an instance of the incremental decoder
610 idec
= WebPINewDecoder(config
? &config
->output
: NULL
);
614 // Finish initialization
615 if (config
!= NULL
) {
616 idec
->params_
.options
= &config
->options
;
621 void WebPIDelete(WebPIDecoder
* idec
) {
622 if (idec
== NULL
) return;
623 if (idec
->dec_
!= NULL
) {
624 if (!idec
->is_lossless_
) {
625 if (idec
->state_
== STATE_VP8_DATA
) {
626 // Synchronize the thread, clean-up and check for errors.
627 VP8ExitCritical((VP8Decoder
*)idec
->dec_
, &idec
->io_
);
629 VP8Delete((VP8Decoder
*)idec
->dec_
);
631 VP8LDelete((VP8LDecoder
*)idec
->dec_
);
634 ClearMemBuffer(&idec
->mem_
);
635 WebPFreeDecBuffer(&idec
->output_
);
639 //------------------------------------------------------------------------------
640 // Wrapper toward WebPINewDecoder
642 WebPIDecoder
* WebPINewRGB(WEBP_CSP_MODE mode
, uint8_t* output_buffer
,
643 size_t output_buffer_size
, int output_stride
) {
644 const int is_external_memory
= (output_buffer
!= NULL
);
647 if (mode
>= MODE_YUV
) return NULL
;
648 if (!is_external_memory
) { // Overwrite parameters to sane values.
649 output_buffer_size
= 0;
651 } else { // A buffer was passed. Validate the other params.
652 if (output_stride
== 0 || output_buffer_size
== 0) {
653 return NULL
; // invalid parameter.
656 idec
= WebPINewDecoder(NULL
);
657 if (idec
== NULL
) return NULL
;
658 idec
->output_
.colorspace
= mode
;
659 idec
->output_
.is_external_memory
= is_external_memory
;
660 idec
->output_
.u
.RGBA
.rgba
= output_buffer
;
661 idec
->output_
.u
.RGBA
.stride
= output_stride
;
662 idec
->output_
.u
.RGBA
.size
= output_buffer_size
;
666 WebPIDecoder
* WebPINewYUVA(uint8_t* luma
, size_t luma_size
, int luma_stride
,
667 uint8_t* u
, size_t u_size
, int u_stride
,
668 uint8_t* v
, size_t v_size
, int v_stride
,
669 uint8_t* a
, size_t a_size
, int a_stride
) {
670 const int is_external_memory
= (luma
!= NULL
);
672 WEBP_CSP_MODE colorspace
;
674 if (!is_external_memory
) { // Overwrite parameters to sane values.
675 luma_size
= u_size
= v_size
= a_size
= 0;
676 luma_stride
= u_stride
= v_stride
= a_stride
= 0;
678 colorspace
= MODE_YUVA
;
679 } else { // A luma buffer was passed. Validate the other parameters.
680 if (u
== NULL
|| v
== NULL
) return NULL
;
681 if (luma_size
== 0 || u_size
== 0 || v_size
== 0) return NULL
;
682 if (luma_stride
== 0 || u_stride
== 0 || v_stride
== 0) return NULL
;
684 if (a_size
== 0 || a_stride
== 0) return NULL
;
686 colorspace
= (a
== NULL
) ? MODE_YUV
: MODE_YUVA
;
689 idec
= WebPINewDecoder(NULL
);
690 if (idec
== NULL
) return NULL
;
692 idec
->output_
.colorspace
= colorspace
;
693 idec
->output_
.is_external_memory
= is_external_memory
;
694 idec
->output_
.u
.YUVA
.y
= luma
;
695 idec
->output_
.u
.YUVA
.y_stride
= luma_stride
;
696 idec
->output_
.u
.YUVA
.y_size
= luma_size
;
697 idec
->output_
.u
.YUVA
.u
= u
;
698 idec
->output_
.u
.YUVA
.u_stride
= u_stride
;
699 idec
->output_
.u
.YUVA
.u_size
= u_size
;
700 idec
->output_
.u
.YUVA
.v
= v
;
701 idec
->output_
.u
.YUVA
.v_stride
= v_stride
;
702 idec
->output_
.u
.YUVA
.v_size
= v_size
;
703 idec
->output_
.u
.YUVA
.a
= a
;
704 idec
->output_
.u
.YUVA
.a_stride
= a_stride
;
705 idec
->output_
.u
.YUVA
.a_size
= a_size
;
709 WebPIDecoder
* WebPINewYUV(uint8_t* luma
, size_t luma_size
, int luma_stride
,
710 uint8_t* u
, size_t u_size
, int u_stride
,
711 uint8_t* v
, size_t v_size
, int v_stride
) {
712 return WebPINewYUVA(luma
, luma_size
, luma_stride
,
718 //------------------------------------------------------------------------------
720 static VP8StatusCode
IDecCheckStatus(const WebPIDecoder
* const idec
) {
722 if (idec
->state_
== STATE_ERROR
) {
723 return VP8_STATUS_BITSTREAM_ERROR
;
725 if (idec
->state_
== STATE_DONE
) {
726 return VP8_STATUS_OK
;
728 return VP8_STATUS_SUSPENDED
;
731 VP8StatusCode
WebPIAppend(WebPIDecoder
* idec
,
732 const uint8_t* data
, size_t data_size
) {
733 VP8StatusCode status
;
734 if (idec
== NULL
|| data
== NULL
) {
735 return VP8_STATUS_INVALID_PARAM
;
737 status
= IDecCheckStatus(idec
);
738 if (status
!= VP8_STATUS_SUSPENDED
) {
741 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
742 if (!CheckMemBufferMode(&idec
->mem_
, MEM_MODE_APPEND
)) {
743 return VP8_STATUS_INVALID_PARAM
;
745 // Append data to memory buffer
746 if (!AppendToMemBuffer(idec
, data
, data_size
)) {
747 return VP8_STATUS_OUT_OF_MEMORY
;
749 return IDecode(idec
);
752 VP8StatusCode
WebPIUpdate(WebPIDecoder
* idec
,
753 const uint8_t* data
, size_t data_size
) {
754 VP8StatusCode status
;
755 if (idec
== NULL
|| data
== NULL
) {
756 return VP8_STATUS_INVALID_PARAM
;
758 status
= IDecCheckStatus(idec
);
759 if (status
!= VP8_STATUS_SUSPENDED
) {
762 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
763 if (!CheckMemBufferMode(&idec
->mem_
, MEM_MODE_MAP
)) {
764 return VP8_STATUS_INVALID_PARAM
;
766 // Make the memory buffer point to the new buffer
767 if (!RemapMemBuffer(idec
, data
, data_size
)) {
768 return VP8_STATUS_INVALID_PARAM
;
770 return IDecode(idec
);
773 //------------------------------------------------------------------------------
775 static const WebPDecBuffer
* GetOutputBuffer(const WebPIDecoder
* const idec
) {
776 if (idec
== NULL
|| idec
->dec_
== NULL
) {
779 if (idec
->state_
<= STATE_VP8_PARTS0
) {
782 return idec
->params_
.output
;
785 const WebPDecBuffer
* WebPIDecodedArea(const WebPIDecoder
* idec
,
787 int* width
, int* height
) {
788 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
789 if (left
!= NULL
) *left
= 0;
790 if (top
!= NULL
) *top
= 0;
791 // TODO(skal): later include handling of rotations.
793 if (width
!= NULL
) *width
= src
->width
;
794 if (height
!= NULL
) *height
= idec
->params_
.last_y
;
796 if (width
!= NULL
) *width
= 0;
797 if (height
!= NULL
) *height
= 0;
802 uint8_t* WebPIDecGetRGB(const WebPIDecoder
* idec
, int* last_y
,
803 int* width
, int* height
, int* stride
) {
804 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
805 if (src
== NULL
) return NULL
;
806 if (src
->colorspace
>= MODE_YUV
) {
810 if (last_y
!= NULL
) *last_y
= idec
->params_
.last_y
;
811 if (width
!= NULL
) *width
= src
->width
;
812 if (height
!= NULL
) *height
= src
->height
;
813 if (stride
!= NULL
) *stride
= src
->u
.RGBA
.stride
;
815 return src
->u
.RGBA
.rgba
;
818 uint8_t* WebPIDecGetYUVA(const WebPIDecoder
* idec
, int* last_y
,
819 uint8_t** u
, uint8_t** v
, uint8_t** a
,
820 int* width
, int* height
,
821 int* stride
, int* uv_stride
, int* a_stride
) {
822 const WebPDecBuffer
* const src
= GetOutputBuffer(idec
);
823 if (src
== NULL
) return NULL
;
824 if (src
->colorspace
< MODE_YUV
) {
828 if (last_y
!= NULL
) *last_y
= idec
->params_
.last_y
;
829 if (u
!= NULL
) *u
= src
->u
.YUVA
.u
;
830 if (v
!= NULL
) *v
= src
->u
.YUVA
.v
;
831 if (a
!= NULL
) *a
= src
->u
.YUVA
.a
;
832 if (width
!= NULL
) *width
= src
->width
;
833 if (height
!= NULL
) *height
= src
->height
;
834 if (stride
!= NULL
) *stride
= src
->u
.YUVA
.y_stride
;
835 if (uv_stride
!= NULL
) *uv_stride
= src
->u
.YUVA
.u_stride
;
836 if (a_stride
!= NULL
) *a_stride
= src
->u
.YUVA
.a_stride
;
838 return src
->u
.YUVA
.y
;
841 int WebPISetIOHooks(WebPIDecoder
* const idec
,
843 VP8IoSetupHook setup
,
844 VP8IoTeardownHook teardown
,
846 if (idec
== NULL
|| idec
->state_
> STATE_WEBP_HEADER
) {
851 idec
->io_
.setup
= setup
;
852 idec
->io_
.teardown
= teardown
;
853 idec
->io_
.opaque
= user_data
;