1 | // Copyright 2011 Google Inc. All Rights Reserved. |
2 | // |
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 | // ----------------------------------------------------------------------------- |
9 | // |
10 | // Incremental decoding |
11 | // |
12 | // Author: somnath@google.com (Somnath Banerjee) |
13 | |
14 | #include <assert.h> |
15 | #include <string.h> |
16 | #include <stdlib.h> |
17 | |
18 | #include "./alphai_dec.h" |
19 | #include "./webpi_dec.h" |
20 | #include "./vp8i_dec.h" |
21 | #include "../utils/utils.h" |
22 | |
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 |
27 | |
28 | //------------------------------------------------------------------------------ |
29 | // Data structures for memory and states |
30 | |
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. |
35 | typedef enum { |
36 | , // All the data before that of the VP8/VP8L chunk. |
37 | , // The VP8 Frame header (within the VP8 chunk). |
38 | STATE_VP8_PARTS0, |
39 | STATE_VP8_DATA, |
40 | , |
41 | STATE_VP8L_DATA, |
42 | STATE_DONE, |
43 | STATE_ERROR |
44 | } DecState; |
45 | |
46 | // Operating state for the MemBuffer |
47 | typedef enum { |
48 | MEM_MODE_NONE = 0, |
49 | MEM_MODE_APPEND, |
50 | MEM_MODE_MAP |
51 | } MemBufferMode; |
52 | |
53 | // storage for partition #0 and partial data (in a rolling fashion) |
54 | typedef struct { |
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() |
60 | |
61 | size_t part0_size_; // size of partition #0 |
62 | const uint8_t* part0_buf_; // buffer to store partition #0 |
63 | } MemBuffer; |
64 | |
65 | struct WebPIDecoder { |
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 |
70 | VP8Io io_; |
71 | |
72 | MemBuffer mem_; // input memory buffer. |
73 | WebPDecBuffer output_; // output buffer (when no external one is supplied, |
74 | // or if the external one has slow-memory) |
75 | WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually. |
76 | size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. |
77 | |
78 | int last_mb_y_; // last row reached for intra-mode decoding |
79 | }; |
80 | |
81 | // MB context to restore in case VP8DecodeMB() fails |
82 | typedef struct { |
83 | VP8MB left_; |
84 | VP8MB info_; |
85 | VP8BitReader token_br_; |
86 | } MBContext; |
87 | |
88 | //------------------------------------------------------------------------------ |
89 | // MemBuffer: incoming data handling |
90 | |
91 | static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) { |
92 | return (mem->end_ - mem->start_); |
93 | } |
94 | |
95 | // Check if we need to preserve the compressed alpha data, as it may not have |
96 | // been decoded yet. |
97 | static int NeedCompressedAlpha(const WebPIDecoder* const idec) { |
98 | if (idec->state_ == STATE_WEBP_HEADER) { |
99 | // We haven't parsed the headers yet, so we don't know whether the image is |
100 | // lossy or lossless. This also means that we haven't parsed the ALPH chunk. |
101 | return 0; |
102 | } |
103 | if (idec->is_lossless_) { |
104 | return 0; // ALPH chunk is not present for lossless images. |
105 | } else { |
106 | const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
107 | assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER. |
108 | return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_; |
109 | } |
110 | } |
111 | |
112 | static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { |
113 | MemBuffer* const mem = &idec->mem_; |
114 | const uint8_t* const new_base = mem->buf_ + mem->start_; |
115 | // note: for VP8, setting up idec->io_ is only really needed at the beginning |
116 | // of the decoding, till partition #0 is complete. |
117 | idec->io_.data = new_base; |
118 | idec->io_.data_size = MemDataSize(mem); |
119 | |
120 | if (idec->dec_ != NULL) { |
121 | if (!idec->is_lossless_) { |
122 | VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
123 | const uint32_t last_part = dec->num_parts_minus_one_; |
124 | if (offset != 0) { |
125 | uint32_t p; |
126 | for (p = 0; p <= last_part; ++p) { |
127 | VP8RemapBitReader(dec->parts_ + p, offset); |
128 | } |
129 | // Remap partition #0 data pointer to new offset, but only in MAP |
130 | // mode (in APPEND mode, partition #0 is copied into a fixed memory). |
131 | if (mem->mode_ == MEM_MODE_MAP) { |
132 | VP8RemapBitReader(&dec->br_, offset); |
133 | } |
134 | } |
135 | { |
136 | const uint8_t* const last_start = dec->parts_[last_part].buf_; |
137 | VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start, |
138 | mem->buf_ + mem->end_ - last_start); |
139 | } |
140 | if (NeedCompressedAlpha(idec)) { |
141 | ALPHDecoder* const alph_dec = dec->alph_dec_; |
142 | dec->alpha_data_ += offset; |
143 | if (alph_dec != NULL) { |
144 | if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) { |
145 | VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_; |
146 | assert(alph_vp8l_dec != NULL); |
147 | assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN); |
148 | VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_, |
149 | dec->alpha_data_ + ALPHA_HEADER_LEN, |
150 | dec->alpha_data_size_ - ALPHA_HEADER_LEN); |
151 | } else { // alph_dec->method_ == ALPHA_NO_COMPRESSION |
152 | // Nothing special to do in this case. |
153 | } |
154 | } |
155 | } |
156 | } else { // Resize lossless bitreader |
157 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; |
158 | VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem)); |
159 | } |
160 | } |
161 | } |
162 | |
163 | // Appends data to the end of MemBuffer->buf_. It expands the allocated memory |
164 | // size if required and also updates VP8BitReader's if new memory is allocated. |
165 | static int AppendToMemBuffer(WebPIDecoder* const idec, |
166 | const uint8_t* const data, size_t data_size) { |
167 | VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
168 | MemBuffer* const mem = &idec->mem_; |
169 | const int need_compressed_alpha = NeedCompressedAlpha(idec); |
170 | const uint8_t* const old_start = mem->buf_ + mem->start_; |
171 | const uint8_t* const old_base = |
172 | need_compressed_alpha ? dec->alpha_data_ : old_start; |
173 | assert(mem->mode_ == MEM_MODE_APPEND); |
174 | if (data_size > MAX_CHUNK_PAYLOAD) { |
175 | // security safeguard: trying to allocate more than what the format |
176 | // allows for a chunk should be considered a smoke smell. |
177 | return 0; |
178 | } |
179 | |
180 | if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory |
181 | const size_t new_mem_start = old_start - old_base; |
182 | const size_t current_size = MemDataSize(mem) + new_mem_start; |
183 | const uint64_t new_size = (uint64_t)current_size + data_size; |
184 | const uint64_t = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); |
185 | uint8_t* const new_buf = |
186 | (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf)); |
187 | if (new_buf == NULL) return 0; |
188 | memcpy(new_buf, old_base, current_size); |
189 | WebPSafeFree(mem->buf_); |
190 | mem->buf_ = new_buf; |
191 | mem->buf_size_ = (size_t)extra_size; |
192 | mem->start_ = new_mem_start; |
193 | mem->end_ = current_size; |
194 | } |
195 | |
196 | memcpy(mem->buf_ + mem->end_, data, data_size); |
197 | mem->end_ += data_size; |
198 | assert(mem->end_ <= mem->buf_size_); |
199 | |
200 | DoRemap(idec, mem->buf_ + mem->start_ - old_start); |
201 | return 1; |
202 | } |
203 | |
204 | static int RemapMemBuffer(WebPIDecoder* const idec, |
205 | const uint8_t* const data, size_t data_size) { |
206 | MemBuffer* const mem = &idec->mem_; |
207 | const uint8_t* const old_buf = mem->buf_; |
208 | const uint8_t* const old_start = old_buf + mem->start_; |
209 | assert(mem->mode_ == MEM_MODE_MAP); |
210 | |
211 | if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer! |
212 | |
213 | mem->buf_ = (uint8_t*)data; |
214 | mem->end_ = mem->buf_size_ = data_size; |
215 | |
216 | DoRemap(idec, mem->buf_ + mem->start_ - old_start); |
217 | return 1; |
218 | } |
219 | |
220 | static void InitMemBuffer(MemBuffer* const mem) { |
221 | mem->mode_ = MEM_MODE_NONE; |
222 | mem->buf_ = NULL; |
223 | mem->buf_size_ = 0; |
224 | mem->part0_buf_ = NULL; |
225 | mem->part0_size_ = 0; |
226 | } |
227 | |
228 | static void ClearMemBuffer(MemBuffer* const mem) { |
229 | assert(mem); |
230 | if (mem->mode_ == MEM_MODE_APPEND) { |
231 | WebPSafeFree(mem->buf_); |
232 | WebPSafeFree((void*)mem->part0_buf_); |
233 | } |
234 | } |
235 | |
236 | static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { |
237 | if (mem->mode_ == MEM_MODE_NONE) { |
238 | mem->mode_ = expected; // switch to the expected mode |
239 | } else if (mem->mode_ != expected) { |
240 | return 0; // we mixed the modes => error |
241 | } |
242 | assert(mem->mode_ == expected); // mode is ok |
243 | return 1; |
244 | } |
245 | |
246 | // To be called last. |
247 | static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { |
248 | const WebPDecoderOptions* const options = idec->params_.options; |
249 | WebPDecBuffer* const output = idec->params_.output; |
250 | |
251 | idec->state_ = STATE_DONE; |
252 | if (options != NULL && options->flip) { |
253 | const VP8StatusCode status = WebPFlipBuffer(output); |
254 | if (status != VP8_STATUS_OK) return status; |
255 | } |
256 | if (idec->final_output_ != NULL) { |
257 | WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy |
258 | WebPFreeDecBuffer(&idec->output_); |
259 | *output = *idec->final_output_; |
260 | idec->final_output_ = NULL; |
261 | } |
262 | return VP8_STATUS_OK; |
263 | } |
264 | |
265 | //------------------------------------------------------------------------------ |
266 | // Macroblock-decoding contexts |
267 | |
268 | static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br, |
269 | MBContext* const context) { |
270 | context->left_ = dec->mb_info_[-1]; |
271 | context->info_ = dec->mb_info_[dec->mb_x_]; |
272 | context->token_br_ = *token_br; |
273 | } |
274 | |
275 | static void RestoreContext(const MBContext* context, VP8Decoder* const dec, |
276 | VP8BitReader* const token_br) { |
277 | dec->mb_info_[-1] = context->left_; |
278 | dec->mb_info_[dec->mb_x_] = context->info_; |
279 | *token_br = context->token_br_; |
280 | } |
281 | |
282 | //------------------------------------------------------------------------------ |
283 | |
284 | static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) { |
285 | if (idec->state_ == STATE_VP8_DATA) { |
286 | VP8Io* const io = &idec->io_; |
287 | if (io->teardown != NULL) { |
288 | io->teardown(io); |
289 | } |
290 | } |
291 | idec->state_ = STATE_ERROR; |
292 | return error; |
293 | } |
294 | |
295 | static void ChangeState(WebPIDecoder* const idec, DecState new_state, |
296 | size_t consumed_bytes) { |
297 | MemBuffer* const mem = &idec->mem_; |
298 | idec->state_ = new_state; |
299 | mem->start_ += consumed_bytes; |
300 | assert(mem->start_ <= mem->end_); |
301 | idec->io_.data = mem->buf_ + mem->start_; |
302 | idec->io_.data_size = MemDataSize(mem); |
303 | } |
304 | |
305 | // Headers |
306 | static VP8StatusCode (WebPIDecoder* const idec) { |
307 | MemBuffer* const mem = &idec->mem_; |
308 | const uint8_t* data = mem->buf_ + mem->start_; |
309 | size_t curr_size = MemDataSize(mem); |
310 | VP8StatusCode status; |
311 | WebPHeaderStructure ; |
312 | |
313 | headers.data = data; |
314 | headers.data_size = curr_size; |
315 | headers.have_all_data = 0; |
316 | status = WebPParseHeaders(&headers); |
317 | if (status == VP8_STATUS_NOT_ENOUGH_DATA) { |
318 | return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet. |
319 | } else if (status != VP8_STATUS_OK) { |
320 | return IDecError(idec, status); |
321 | } |
322 | |
323 | idec->chunk_size_ = headers.compressed_size; |
324 | idec->is_lossless_ = headers.is_lossless; |
325 | if (!idec->is_lossless_) { |
326 | VP8Decoder* const dec = VP8New(); |
327 | if (dec == NULL) { |
328 | return VP8_STATUS_OUT_OF_MEMORY; |
329 | } |
330 | idec->dec_ = dec; |
331 | dec->alpha_data_ = headers.alpha_data; |
332 | dec->alpha_data_size_ = headers.alpha_data_size; |
333 | ChangeState(idec, STATE_VP8_HEADER, headers.offset); |
334 | } else { |
335 | VP8LDecoder* const dec = VP8LNew(); |
336 | if (dec == NULL) { |
337 | return VP8_STATUS_OUT_OF_MEMORY; |
338 | } |
339 | idec->dec_ = dec; |
340 | ChangeState(idec, STATE_VP8L_HEADER, headers.offset); |
341 | } |
342 | return VP8_STATUS_OK; |
343 | } |
344 | |
345 | static VP8StatusCode (WebPIDecoder* const idec) { |
346 | const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_; |
347 | const size_t curr_size = MemDataSize(&idec->mem_); |
348 | int width, height; |
349 | uint32_t bits; |
350 | |
351 | if (curr_size < VP8_FRAME_HEADER_SIZE) { |
352 | // Not enough data bytes to extract VP8 Frame Header. |
353 | return VP8_STATUS_SUSPENDED; |
354 | } |
355 | if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) { |
356 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); |
357 | } |
358 | |
359 | bits = data[0] | (data[1] << 8) | (data[2] << 16); |
360 | idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE; |
361 | |
362 | idec->io_.data = data; |
363 | idec->io_.data_size = curr_size; |
364 | idec->state_ = STATE_VP8_PARTS0; |
365 | return VP8_STATUS_OK; |
366 | } |
367 | |
368 | // Partition #0 |
369 | static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) { |
370 | VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
371 | VP8BitReader* const br = &dec->br_; |
372 | const size_t part_size = br->buf_end_ - br->buf_; |
373 | MemBuffer* const mem = &idec->mem_; |
374 | assert(!idec->is_lossless_); |
375 | assert(mem->part0_buf_ == NULL); |
376 | // the following is a format limitation, no need for runtime check: |
377 | assert(part_size <= mem->part0_size_); |
378 | if (part_size == 0) { // can't have zero-size partition #0 |
379 | return VP8_STATUS_BITSTREAM_ERROR; |
380 | } |
381 | if (mem->mode_ == MEM_MODE_APPEND) { |
382 | // We copy and grab ownership of the partition #0 data. |
383 | uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size); |
384 | if (part0_buf == NULL) { |
385 | return VP8_STATUS_OUT_OF_MEMORY; |
386 | } |
387 | memcpy(part0_buf, br->buf_, part_size); |
388 | mem->part0_buf_ = part0_buf; |
389 | VP8BitReaderSetBuffer(br, part0_buf, part_size); |
390 | } else { |
391 | // Else: just keep pointers to the partition #0's data in dec_->br_. |
392 | } |
393 | mem->start_ += part_size; |
394 | return VP8_STATUS_OK; |
395 | } |
396 | |
397 | static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) { |
398 | VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
399 | VP8Io* const io = &idec->io_; |
400 | const WebPDecParams* const params = &idec->params_; |
401 | WebPDecBuffer* const output = params->output; |
402 | |
403 | // Wait till we have enough data for the whole partition #0 |
404 | if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) { |
405 | return VP8_STATUS_SUSPENDED; |
406 | } |
407 | |
408 | if (!VP8GetHeaders(dec, io)) { |
409 | const VP8StatusCode status = dec->status_; |
410 | if (status == VP8_STATUS_SUSPENDED || |
411 | status == VP8_STATUS_NOT_ENOUGH_DATA) { |
412 | // treating NOT_ENOUGH_DATA as SUSPENDED state |
413 | return VP8_STATUS_SUSPENDED; |
414 | } |
415 | return IDecError(idec, status); |
416 | } |
417 | |
418 | // Allocate/Verify output buffer now |
419 | dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, |
420 | output); |
421 | if (dec->status_ != VP8_STATUS_OK) { |
422 | return IDecError(idec, dec->status_); |
423 | } |
424 | // This change must be done before calling VP8InitFrame() |
425 | dec->mt_method_ = VP8GetThreadMethod(params->options, NULL, |
426 | io->width, io->height); |
427 | VP8InitDithering(params->options, dec); |
428 | |
429 | dec->status_ = CopyParts0Data(idec); |
430 | if (dec->status_ != VP8_STATUS_OK) { |
431 | return IDecError(idec, dec->status_); |
432 | } |
433 | |
434 | // Finish setting up the decoding parameters. Will call io->setup(). |
435 | if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) { |
436 | return IDecError(idec, dec->status_); |
437 | } |
438 | |
439 | // Note: past this point, teardown() must always be called |
440 | // in case of error. |
441 | idec->state_ = STATE_VP8_DATA; |
442 | // Allocate memory and prepare everything. |
443 | if (!VP8InitFrame(dec, io)) { |
444 | return IDecError(idec, dec->status_); |
445 | } |
446 | return VP8_STATUS_OK; |
447 | } |
448 | |
449 | // Remaining partitions |
450 | static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { |
451 | VP8Decoder* const dec = (VP8Decoder*)idec->dec_; |
452 | VP8Io* const io = &idec->io_; |
453 | |
454 | assert(dec->ready_); |
455 | for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { |
456 | if (idec->last_mb_y_ != dec->mb_y_) { |
457 | if (!VP8ParseIntraModeRow(&dec->br_, dec)) { |
458 | // note: normally, error shouldn't occur since we already have the whole |
459 | // partition0 available here in DecodeRemaining(). Reaching EOF while |
460 | // reading intra modes really means a BITSTREAM_ERROR. |
461 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); |
462 | } |
463 | idec->last_mb_y_ = dec->mb_y_; |
464 | } |
465 | for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { |
466 | VP8BitReader* const token_br = |
467 | &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; |
468 | MBContext context; |
469 | SaveContext(dec, token_br, &context); |
470 | if (!VP8DecodeMB(dec, token_br)) { |
471 | // We shouldn't fail when MAX_MB data was available |
472 | if (dec->num_parts_minus_one_ == 0 && |
473 | MemDataSize(&idec->mem_) > MAX_MB_SIZE) { |
474 | return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); |
475 | } |
476 | RestoreContext(&context, dec, token_br); |
477 | return VP8_STATUS_SUSPENDED; |
478 | } |
479 | // Release buffer only if there is only one partition |
480 | if (dec->num_parts_minus_one_ == 0) { |
481 | idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; |
482 | assert(idec->mem_.start_ <= idec->mem_.end_); |
483 | } |
484 | } |
485 | VP8InitScanline(dec); // Prepare for next scanline |
486 | |
487 | // Reconstruct, filter and emit the row. |
488 | if (!VP8ProcessRow(dec, io)) { |
489 | return IDecError(idec, VP8_STATUS_USER_ABORT); |
490 | } |
491 | } |
492 | // Synchronize the thread and check for errors. |
493 | if (!VP8ExitCritical(dec, io)) { |
494 | return IDecError(idec, VP8_STATUS_USER_ABORT); |
495 | } |
496 | dec->ready_ = 0; |
497 | return FinishDecoding(idec); |
498 | } |
499 | |
500 | static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, |
501 | VP8StatusCode status) { |
502 | if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) { |
503 | return VP8_STATUS_SUSPENDED; |
504 | } |
505 | return IDecError(idec, status); |
506 | } |
507 | |
508 | static VP8StatusCode (WebPIDecoder* const idec) { |
509 | VP8Io* const io = &idec->io_; |
510 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; |
511 | const WebPDecParams* const params = &idec->params_; |
512 | WebPDecBuffer* const output = params->output; |
513 | size_t curr_size = MemDataSize(&idec->mem_); |
514 | assert(idec->is_lossless_); |
515 | |
516 | // Wait until there's enough data for decoding header. |
517 | if (curr_size < (idec->chunk_size_ >> 3)) { |
518 | dec->status_ = VP8_STATUS_SUSPENDED; |
519 | return ErrorStatusLossless(idec, dec->status_); |
520 | } |
521 | |
522 | if (!VP8LDecodeHeader(dec, io)) { |
523 | if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && |
524 | curr_size < idec->chunk_size_) { |
525 | dec->status_ = VP8_STATUS_SUSPENDED; |
526 | } |
527 | return ErrorStatusLossless(idec, dec->status_); |
528 | } |
529 | // Allocate/verify output buffer now. |
530 | dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options, |
531 | output); |
532 | if (dec->status_ != VP8_STATUS_OK) { |
533 | return IDecError(idec, dec->status_); |
534 | } |
535 | |
536 | idec->state_ = STATE_VP8L_DATA; |
537 | return VP8_STATUS_OK; |
538 | } |
539 | |
540 | static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { |
541 | VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_; |
542 | const size_t curr_size = MemDataSize(&idec->mem_); |
543 | assert(idec->is_lossless_); |
544 | |
545 | // Switch to incremental decoding if we don't have all the bytes available. |
546 | dec->incremental_ = (curr_size < idec->chunk_size_); |
547 | |
548 | if (!VP8LDecodeImage(dec)) { |
549 | return ErrorStatusLossless(idec, dec->status_); |
550 | } |
551 | assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED); |
552 | return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_ |
553 | : FinishDecoding(idec); |
554 | } |
555 | |
556 | // Main decoding loop |
557 | static VP8StatusCode IDecode(WebPIDecoder* idec) { |
558 | VP8StatusCode status = VP8_STATUS_SUSPENDED; |
559 | |
560 | if (idec->state_ == STATE_WEBP_HEADER) { |
561 | status = DecodeWebPHeaders(idec); |
562 | } else { |
563 | if (idec->dec_ == NULL) { |
564 | return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder. |
565 | } |
566 | } |
567 | if (idec->state_ == STATE_VP8_HEADER) { |
568 | status = DecodeVP8FrameHeader(idec); |
569 | } |
570 | if (idec->state_ == STATE_VP8_PARTS0) { |
571 | status = DecodePartition0(idec); |
572 | } |
573 | if (idec->state_ == STATE_VP8_DATA) { |
574 | status = DecodeRemaining(idec); |
575 | } |
576 | if (idec->state_ == STATE_VP8L_HEADER) { |
577 | status = DecodeVP8LHeader(idec); |
578 | } |
579 | if (idec->state_ == STATE_VP8L_DATA) { |
580 | status = DecodeVP8LData(idec); |
581 | } |
582 | return status; |
583 | } |
584 | |
585 | //------------------------------------------------------------------------------ |
586 | // Internal constructor |
587 | |
588 | static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, |
589 | const WebPBitstreamFeatures* const features) { |
590 | WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); |
591 | if (idec == NULL) { |
592 | return NULL; |
593 | } |
594 | |
595 | idec->state_ = STATE_WEBP_HEADER; |
596 | idec->chunk_size_ = 0; |
597 | |
598 | idec->last_mb_y_ = -1; |
599 | |
600 | InitMemBuffer(&idec->mem_); |
601 | WebPInitDecBuffer(&idec->output_); |
602 | VP8InitIo(&idec->io_); |
603 | |
604 | WebPResetDecParams(&idec->params_); |
605 | if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { |
606 | idec->params_.output = &idec->output_; |
607 | idec->final_output_ = output_buffer; |
608 | if (output_buffer != NULL) { |
609 | idec->params_.output->colorspace = output_buffer->colorspace; |
610 | } |
611 | } else { |
612 | idec->params_.output = output_buffer; |
613 | idec->final_output_ = NULL; |
614 | } |
615 | WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. |
616 | |
617 | return idec; |
618 | } |
619 | |
620 | //------------------------------------------------------------------------------ |
621 | // Public functions |
622 | |
623 | WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { |
624 | return NewDecoder(output_buffer, NULL); |
625 | } |
626 | |
627 | WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, |
628 | WebPDecoderConfig* config) { |
629 | WebPIDecoder* idec; |
630 | WebPBitstreamFeatures tmp_features; |
631 | WebPBitstreamFeatures* const features = |
632 | (config == NULL) ? &tmp_features : &config->input; |
633 | memset(&tmp_features, 0, sizeof(tmp_features)); |
634 | |
635 | // Parse the bitstream's features, if requested: |
636 | if (data != NULL && data_size > 0) { |
637 | if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { |
638 | return NULL; |
639 | } |
640 | } |
641 | |
642 | // Create an instance of the incremental decoder |
643 | idec = (config != NULL) ? NewDecoder(&config->output, features) |
644 | : NewDecoder(NULL, features); |
645 | if (idec == NULL) { |
646 | return NULL; |
647 | } |
648 | // Finish initialization |
649 | if (config != NULL) { |
650 | idec->params_.options = &config->options; |
651 | } |
652 | return idec; |
653 | } |
654 | |
655 | void WebPIDelete(WebPIDecoder* idec) { |
656 | if (idec == NULL) return; |
657 | if (idec->dec_ != NULL) { |
658 | if (!idec->is_lossless_) { |
659 | if (idec->state_ == STATE_VP8_DATA) { |
660 | // Synchronize the thread, clean-up and check for errors. |
661 | VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_); |
662 | } |
663 | VP8Delete((VP8Decoder*)idec->dec_); |
664 | } else { |
665 | VP8LDelete((VP8LDecoder*)idec->dec_); |
666 | } |
667 | } |
668 | ClearMemBuffer(&idec->mem_); |
669 | WebPFreeDecBuffer(&idec->output_); |
670 | WebPSafeFree(idec); |
671 | } |
672 | |
673 | //------------------------------------------------------------------------------ |
674 | // Wrapper toward WebPINewDecoder |
675 | |
676 | WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer, |
677 | size_t output_buffer_size, int output_stride) { |
678 | const int is_external_memory = (output_buffer != NULL) ? 1 : 0; |
679 | WebPIDecoder* idec; |
680 | |
681 | if (mode >= MODE_YUV) return NULL; |
682 | if (is_external_memory == 0) { // Overwrite parameters to sane values. |
683 | output_buffer_size = 0; |
684 | output_stride = 0; |
685 | } else { // A buffer was passed. Validate the other params. |
686 | if (output_stride == 0 || output_buffer_size == 0) { |
687 | return NULL; // invalid parameter. |
688 | } |
689 | } |
690 | idec = WebPINewDecoder(NULL); |
691 | if (idec == NULL) return NULL; |
692 | idec->output_.colorspace = mode; |
693 | idec->output_.is_external_memory = is_external_memory; |
694 | idec->output_.u.RGBA.rgba = output_buffer; |
695 | idec->output_.u.RGBA.stride = output_stride; |
696 | idec->output_.u.RGBA.size = output_buffer_size; |
697 | return idec; |
698 | } |
699 | |
700 | WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride, |
701 | uint8_t* u, size_t u_size, int u_stride, |
702 | uint8_t* v, size_t v_size, int v_stride, |
703 | uint8_t* a, size_t a_size, int a_stride) { |
704 | const int is_external_memory = (luma != NULL) ? 1 : 0; |
705 | WebPIDecoder* idec; |
706 | WEBP_CSP_MODE colorspace; |
707 | |
708 | if (is_external_memory == 0) { // Overwrite parameters to sane values. |
709 | luma_size = u_size = v_size = a_size = 0; |
710 | luma_stride = u_stride = v_stride = a_stride = 0; |
711 | u = v = a = NULL; |
712 | colorspace = MODE_YUVA; |
713 | } else { // A luma buffer was passed. Validate the other parameters. |
714 | if (u == NULL || v == NULL) return NULL; |
715 | if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL; |
716 | if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL; |
717 | if (a != NULL) { |
718 | if (a_size == 0 || a_stride == 0) return NULL; |
719 | } |
720 | colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA; |
721 | } |
722 | |
723 | idec = WebPINewDecoder(NULL); |
724 | if (idec == NULL) return NULL; |
725 | |
726 | idec->output_.colorspace = colorspace; |
727 | idec->output_.is_external_memory = is_external_memory; |
728 | idec->output_.u.YUVA.y = luma; |
729 | idec->output_.u.YUVA.y_stride = luma_stride; |
730 | idec->output_.u.YUVA.y_size = luma_size; |
731 | idec->output_.u.YUVA.u = u; |
732 | idec->output_.u.YUVA.u_stride = u_stride; |
733 | idec->output_.u.YUVA.u_size = u_size; |
734 | idec->output_.u.YUVA.v = v; |
735 | idec->output_.u.YUVA.v_stride = v_stride; |
736 | idec->output_.u.YUVA.v_size = v_size; |
737 | idec->output_.u.YUVA.a = a; |
738 | idec->output_.u.YUVA.a_stride = a_stride; |
739 | idec->output_.u.YUVA.a_size = a_size; |
740 | return idec; |
741 | } |
742 | |
743 | WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride, |
744 | uint8_t* u, size_t u_size, int u_stride, |
745 | uint8_t* v, size_t v_size, int v_stride) { |
746 | return WebPINewYUVA(luma, luma_size, luma_stride, |
747 | u, u_size, u_stride, |
748 | v, v_size, v_stride, |
749 | NULL, 0, 0); |
750 | } |
751 | |
752 | //------------------------------------------------------------------------------ |
753 | |
754 | static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) { |
755 | assert(idec); |
756 | if (idec->state_ == STATE_ERROR) { |
757 | return VP8_STATUS_BITSTREAM_ERROR; |
758 | } |
759 | if (idec->state_ == STATE_DONE) { |
760 | return VP8_STATUS_OK; |
761 | } |
762 | return VP8_STATUS_SUSPENDED; |
763 | } |
764 | |
765 | VP8StatusCode WebPIAppend(WebPIDecoder* idec, |
766 | const uint8_t* data, size_t data_size) { |
767 | VP8StatusCode status; |
768 | if (idec == NULL || data == NULL) { |
769 | return VP8_STATUS_INVALID_PARAM; |
770 | } |
771 | status = IDecCheckStatus(idec); |
772 | if (status != VP8_STATUS_SUSPENDED) { |
773 | return status; |
774 | } |
775 | // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. |
776 | if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { |
777 | return VP8_STATUS_INVALID_PARAM; |
778 | } |
779 | // Append data to memory buffer |
780 | if (!AppendToMemBuffer(idec, data, data_size)) { |
781 | return VP8_STATUS_OUT_OF_MEMORY; |
782 | } |
783 | return IDecode(idec); |
784 | } |
785 | |
786 | VP8StatusCode WebPIUpdate(WebPIDecoder* idec, |
787 | const uint8_t* data, size_t data_size) { |
788 | VP8StatusCode status; |
789 | if (idec == NULL || data == NULL) { |
790 | return VP8_STATUS_INVALID_PARAM; |
791 | } |
792 | status = IDecCheckStatus(idec); |
793 | if (status != VP8_STATUS_SUSPENDED) { |
794 | return status; |
795 | } |
796 | // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. |
797 | if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { |
798 | return VP8_STATUS_INVALID_PARAM; |
799 | } |
800 | // Make the memory buffer point to the new buffer |
801 | if (!RemapMemBuffer(idec, data, data_size)) { |
802 | return VP8_STATUS_INVALID_PARAM; |
803 | } |
804 | return IDecode(idec); |
805 | } |
806 | |
807 | //------------------------------------------------------------------------------ |
808 | |
809 | static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { |
810 | if (idec == NULL || idec->dec_ == NULL) { |
811 | return NULL; |
812 | } |
813 | if (idec->state_ <= STATE_VP8_PARTS0) { |
814 | return NULL; |
815 | } |
816 | if (idec->final_output_ != NULL) { |
817 | return NULL; // not yet slow-copied |
818 | } |
819 | return idec->params_.output; |
820 | } |
821 | |
822 | const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, |
823 | int* left, int* top, |
824 | int* width, int* height) { |
825 | const WebPDecBuffer* const src = GetOutputBuffer(idec); |
826 | if (left != NULL) *left = 0; |
827 | if (top != NULL) *top = 0; |
828 | if (src != NULL) { |
829 | if (width != NULL) *width = src->width; |
830 | if (height != NULL) *height = idec->params_.last_y; |
831 | } else { |
832 | if (width != NULL) *width = 0; |
833 | if (height != NULL) *height = 0; |
834 | } |
835 | return src; |
836 | } |
837 | |
838 | uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y, |
839 | int* width, int* height, int* stride) { |
840 | const WebPDecBuffer* const src = GetOutputBuffer(idec); |
841 | if (src == NULL) return NULL; |
842 | if (src->colorspace >= MODE_YUV) { |
843 | return NULL; |
844 | } |
845 | |
846 | if (last_y != NULL) *last_y = idec->params_.last_y; |
847 | if (width != NULL) *width = src->width; |
848 | if (height != NULL) *height = src->height; |
849 | if (stride != NULL) *stride = src->u.RGBA.stride; |
850 | |
851 | return src->u.RGBA.rgba; |
852 | } |
853 | |
854 | uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y, |
855 | uint8_t** u, uint8_t** v, uint8_t** a, |
856 | int* width, int* height, |
857 | int* stride, int* uv_stride, int* a_stride) { |
858 | const WebPDecBuffer* const src = GetOutputBuffer(idec); |
859 | if (src == NULL) return NULL; |
860 | if (src->colorspace < MODE_YUV) { |
861 | return NULL; |
862 | } |
863 | |
864 | if (last_y != NULL) *last_y = idec->params_.last_y; |
865 | if (u != NULL) *u = src->u.YUVA.u; |
866 | if (v != NULL) *v = src->u.YUVA.v; |
867 | if (a != NULL) *a = src->u.YUVA.a; |
868 | if (width != NULL) *width = src->width; |
869 | if (height != NULL) *height = src->height; |
870 | if (stride != NULL) *stride = src->u.YUVA.y_stride; |
871 | if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride; |
872 | if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride; |
873 | |
874 | return src->u.YUVA.y; |
875 | } |
876 | |
877 | int WebPISetIOHooks(WebPIDecoder* const idec, |
878 | VP8IoPutHook put, |
879 | VP8IoSetupHook setup, |
880 | VP8IoTeardownHook teardown, |
881 | void* user_data) { |
882 | if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) { |
883 | return 0; |
884 | } |
885 | |
886 | idec->io_.put = put; |
887 | idec->io_.setup = setup; |
888 | idec->io_.teardown = teardown; |
889 | idec->io_.opaque = user_data; |
890 | |
891 | return 1; |
892 | } |
893 | |