Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/3rdparty/libwebp/src/dec/webp_dec.c
16358 views
1
// Copyright 2010 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
// Main decoding functions for WEBP images.
11
//
12
// Author: Skal ([email protected])
13
14
#include <stdlib.h>
15
16
#include "src/dec/vp8i_dec.h"
17
#include "src/dec/vp8li_dec.h"
18
#include "src/dec/webpi_dec.h"
19
#include "src/utils/utils.h"
20
#include "src/webp/mux_types.h" // ALPHA_FLAG
21
22
//------------------------------------------------------------------------------
23
// RIFF layout is:
24
// Offset tag
25
// 0...3 "RIFF" 4-byte tag
26
// 4...7 size of image data (including metadata) starting at offset 8
27
// 8...11 "WEBP" our form-type signature
28
// The RIFF container (12 bytes) is followed by appropriate chunks:
29
// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
30
// 16..19 size of the raw VP8 image data, starting at offset 20
31
// 20.... the VP8 bytes
32
// Or,
33
// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
34
// 16..19 size of the raw VP8L image data, starting at offset 20
35
// 20.... the VP8L bytes
36
// Or,
37
// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
38
// 16..19 size of the VP8X chunk starting at offset 20.
39
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
40
// 24..26 Width of the Canvas Image.
41
// 27..29 Height of the Canvas Image.
42
// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L,
43
// XMP, EXIF ...)
44
// All sizes are in little-endian order.
45
// Note: chunk data size must be padded to multiple of 2 when written.
46
47
// Validates the RIFF container (if detected) and skips over it.
48
// If a RIFF container is detected, returns:
49
// VP8_STATUS_BITSTREAM_ERROR for invalid header,
50
// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true,
51
// and VP8_STATUS_OK otherwise.
52
// In case there are not enough bytes (partial RIFF container), return 0 for
53
// *riff_size. Else return the RIFF size extracted from the header.
54
static VP8StatusCode ParseRIFF(const uint8_t** const data,
55
size_t* const data_size, int have_all_data,
56
size_t* const riff_size) {
57
assert(data != NULL);
58
assert(data_size != NULL);
59
assert(riff_size != NULL);
60
61
*riff_size = 0; // Default: no RIFF present.
62
if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) {
63
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
64
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
65
} else {
66
const uint32_t size = GetLE32(*data + TAG_SIZE);
67
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
68
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
69
return VP8_STATUS_BITSTREAM_ERROR;
70
}
71
if (size > MAX_CHUNK_PAYLOAD) {
72
return VP8_STATUS_BITSTREAM_ERROR;
73
}
74
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
75
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
76
}
77
// We have a RIFF container. Skip it.
78
*riff_size = size;
79
*data += RIFF_HEADER_SIZE;
80
*data_size -= RIFF_HEADER_SIZE;
81
}
82
}
83
return VP8_STATUS_OK;
84
}
85
86
// Validates the VP8X header and skips over it.
87
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
88
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
89
// VP8_STATUS_OK otherwise.
90
// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
91
// *height_ptr and *flags_ptr are set to the corresponding values extracted
92
// from the VP8X chunk.
93
static VP8StatusCode ParseVP8X(const uint8_t** const data,
94
size_t* const data_size,
95
int* const found_vp8x,
96
int* const width_ptr, int* const height_ptr,
97
uint32_t* const flags_ptr) {
98
const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
99
assert(data != NULL);
100
assert(data_size != NULL);
101
assert(found_vp8x != NULL);
102
103
*found_vp8x = 0;
104
105
if (*data_size < CHUNK_HEADER_SIZE) {
106
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
107
}
108
109
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
110
int width, height;
111
uint32_t flags;
112
const uint32_t chunk_size = GetLE32(*data + TAG_SIZE);
113
if (chunk_size != VP8X_CHUNK_SIZE) {
114
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
115
}
116
117
// Verify if enough data is available to validate the VP8X chunk.
118
if (*data_size < vp8x_size) {
119
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
120
}
121
flags = GetLE32(*data + 8);
122
width = 1 + GetLE24(*data + 12);
123
height = 1 + GetLE24(*data + 15);
124
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
125
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
126
}
127
128
if (flags_ptr != NULL) *flags_ptr = flags;
129
if (width_ptr != NULL) *width_ptr = width;
130
if (height_ptr != NULL) *height_ptr = height;
131
// Skip over VP8X header bytes.
132
*data += vp8x_size;
133
*data_size -= vp8x_size;
134
*found_vp8x = 1;
135
}
136
return VP8_STATUS_OK;
137
}
138
139
// Skips to the next VP8/VP8L chunk header in the data given the size of the
140
// RIFF chunk 'riff_size'.
141
// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
142
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
143
// VP8_STATUS_OK otherwise.
144
// If an alpha chunk is found, *alpha_data and *alpha_size are set
145
// appropriately.
146
static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
147
size_t* const data_size,
148
size_t const riff_size,
149
const uint8_t** const alpha_data,
150
size_t* const alpha_size) {
151
const uint8_t* buf;
152
size_t buf_size;
153
uint32_t total_size = TAG_SIZE + // "WEBP".
154
CHUNK_HEADER_SIZE + // "VP8Xnnnn".
155
VP8X_CHUNK_SIZE; // data.
156
assert(data != NULL);
157
assert(data_size != NULL);
158
buf = *data;
159
buf_size = *data_size;
160
161
assert(alpha_data != NULL);
162
assert(alpha_size != NULL);
163
*alpha_data = NULL;
164
*alpha_size = 0;
165
166
while (1) {
167
uint32_t chunk_size;
168
uint32_t disk_chunk_size; // chunk_size with padding
169
170
*data = buf;
171
*data_size = buf_size;
172
173
if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data.
174
return VP8_STATUS_NOT_ENOUGH_DATA;
175
}
176
177
chunk_size = GetLE32(buf + TAG_SIZE);
178
if (chunk_size > MAX_CHUNK_PAYLOAD) {
179
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
180
}
181
// For odd-sized chunk-payload, there's one byte padding at the end.
182
disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
183
total_size += disk_chunk_size;
184
185
// Check that total bytes skipped so far does not exceed riff_size.
186
if (riff_size > 0 && (total_size > riff_size)) {
187
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
188
}
189
190
// Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
191
// parsed all the optional chunks.
192
// Note: This check must occur before the check 'buf_size < disk_chunk_size'
193
// below to allow incomplete VP8/VP8L chunks.
194
if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
195
!memcmp(buf, "VP8L", TAG_SIZE)) {
196
return VP8_STATUS_OK;
197
}
198
199
if (buf_size < disk_chunk_size) { // Insufficient data.
200
return VP8_STATUS_NOT_ENOUGH_DATA;
201
}
202
203
if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
204
*alpha_data = buf + CHUNK_HEADER_SIZE;
205
*alpha_size = chunk_size;
206
}
207
208
// We have a full and valid chunk; skip it.
209
buf += disk_chunk_size;
210
buf_size -= disk_chunk_size;
211
}
212
}
213
214
// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
215
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
216
// riff_size) VP8/VP8L header,
217
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
218
// VP8_STATUS_OK otherwise.
219
// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
220
// extracted from the VP8/VP8L chunk header.
221
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
222
static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
223
size_t* const data_size, int have_all_data,
224
size_t riff_size, size_t* const chunk_size,
225
int* const is_lossless) {
226
const uint8_t* const data = *data_ptr;
227
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
228
const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
229
const uint32_t minimal_size =
230
TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
231
// "WEBP" + "VP8Lnnnn"
232
assert(data != NULL);
233
assert(data_size != NULL);
234
assert(chunk_size != NULL);
235
assert(is_lossless != NULL);
236
237
if (*data_size < CHUNK_HEADER_SIZE) {
238
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
239
}
240
241
if (is_vp8 || is_vp8l) {
242
// Bitstream contains VP8/VP8L header.
243
const uint32_t size = GetLE32(data + TAG_SIZE);
244
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
245
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
246
}
247
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
248
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
249
}
250
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
251
*chunk_size = size;
252
*data_ptr += CHUNK_HEADER_SIZE;
253
*data_size -= CHUNK_HEADER_SIZE;
254
*is_lossless = is_vp8l;
255
} else {
256
// Raw VP8/VP8L bitstream (no header).
257
*is_lossless = VP8LCheckSignature(data, *data_size);
258
*chunk_size = *data_size;
259
}
260
261
return VP8_STATUS_OK;
262
}
263
264
//------------------------------------------------------------------------------
265
266
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
267
// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
268
// minimal amount will be read to fetch the remaining parameters.
269
// If 'headers' is non-NULL this function will attempt to locate both alpha
270
// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
271
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
272
// considered valid by this function:
273
// RIFF + VP8(L)
274
// RIFF + VP8X + (optional chunks) + VP8(L)
275
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
276
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
277
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
278
size_t data_size,
279
int* const width,
280
int* const height,
281
int* const has_alpha,
282
int* const has_animation,
283
int* const format,
284
WebPHeaderStructure* const headers) {
285
int canvas_width = 0;
286
int canvas_height = 0;
287
int image_width = 0;
288
int image_height = 0;
289
int found_riff = 0;
290
int found_vp8x = 0;
291
int animation_present = 0;
292
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
293
294
VP8StatusCode status;
295
WebPHeaderStructure hdrs;
296
297
if (data == NULL || data_size < RIFF_HEADER_SIZE) {
298
return VP8_STATUS_NOT_ENOUGH_DATA;
299
}
300
memset(&hdrs, 0, sizeof(hdrs));
301
hdrs.data = data;
302
hdrs.data_size = data_size;
303
304
// Skip over RIFF header.
305
status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
306
if (status != VP8_STATUS_OK) {
307
return status; // Wrong RIFF header / insufficient data.
308
}
309
found_riff = (hdrs.riff_size > 0);
310
311
// Skip over VP8X.
312
{
313
uint32_t flags = 0;
314
status = ParseVP8X(&data, &data_size, &found_vp8x,
315
&canvas_width, &canvas_height, &flags);
316
if (status != VP8_STATUS_OK) {
317
return status; // Wrong VP8X / insufficient data.
318
}
319
animation_present = !!(flags & ANIMATION_FLAG);
320
if (!found_riff && found_vp8x) {
321
// Note: This restriction may be removed in the future, if it becomes
322
// necessary to send VP8X chunk to the decoder.
323
return VP8_STATUS_BITSTREAM_ERROR;
324
}
325
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
326
if (has_animation != NULL) *has_animation = animation_present;
327
if (format != NULL) *format = 0; // default = undefined
328
329
image_width = canvas_width;
330
image_height = canvas_height;
331
if (found_vp8x && animation_present && headers == NULL) {
332
status = VP8_STATUS_OK;
333
goto ReturnWidthHeight; // Just return features from VP8X header.
334
}
335
}
336
337
if (data_size < TAG_SIZE) {
338
status = VP8_STATUS_NOT_ENOUGH_DATA;
339
goto ReturnWidthHeight;
340
}
341
342
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
343
if ((found_riff && found_vp8x) ||
344
(!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
345
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
346
&hdrs.alpha_data, &hdrs.alpha_data_size);
347
if (status != VP8_STATUS_OK) {
348
goto ReturnWidthHeight; // Invalid chunk size / insufficient data.
349
}
350
}
351
352
// Skip over VP8/VP8L header.
353
status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size,
354
&hdrs.compressed_size, &hdrs.is_lossless);
355
if (status != VP8_STATUS_OK) {
356
goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data.
357
}
358
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
359
return VP8_STATUS_BITSTREAM_ERROR;
360
}
361
362
if (format != NULL && !animation_present) {
363
*format = hdrs.is_lossless ? 2 : 1;
364
}
365
366
if (!hdrs.is_lossless) {
367
if (data_size < VP8_FRAME_HEADER_SIZE) {
368
status = VP8_STATUS_NOT_ENOUGH_DATA;
369
goto ReturnWidthHeight;
370
}
371
// Validates raw VP8 data.
372
if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
373
&image_width, &image_height)) {
374
return VP8_STATUS_BITSTREAM_ERROR;
375
}
376
} else {
377
if (data_size < VP8L_FRAME_HEADER_SIZE) {
378
status = VP8_STATUS_NOT_ENOUGH_DATA;
379
goto ReturnWidthHeight;
380
}
381
// Validates raw VP8L data.
382
if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
383
return VP8_STATUS_BITSTREAM_ERROR;
384
}
385
}
386
// Validates image size coherency.
387
if (found_vp8x) {
388
if (canvas_width != image_width || canvas_height != image_height) {
389
return VP8_STATUS_BITSTREAM_ERROR;
390
}
391
}
392
if (headers != NULL) {
393
*headers = hdrs;
394
headers->offset = data - headers->data;
395
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
396
assert(headers->offset == headers->data_size - data_size);
397
}
398
ReturnWidthHeight:
399
if (status == VP8_STATUS_OK ||
400
(status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
401
if (has_alpha != NULL) {
402
// If the data did not contain a VP8X/VP8L chunk the only definitive way
403
// to set this is by looking for alpha data (from an ALPH chunk).
404
*has_alpha |= (hdrs.alpha_data != NULL);
405
}
406
if (width != NULL) *width = image_width;
407
if (height != NULL) *height = image_height;
408
return VP8_STATUS_OK;
409
} else {
410
return status;
411
}
412
}
413
414
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
415
// status is marked volatile as a workaround for a clang-3.8 (aarch64) bug
416
volatile VP8StatusCode status;
417
int has_animation = 0;
418
assert(headers != NULL);
419
// fill out headers, ignore width/height/has_alpha.
420
status = ParseHeadersInternal(headers->data, headers->data_size,
421
NULL, NULL, NULL, &has_animation,
422
NULL, headers);
423
if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
424
// The WebPDemux API + libwebp can be used to decode individual
425
// uncomposited frames or the WebPAnimDecoder can be used to fully
426
// reconstruct them (see webp/demux.h).
427
if (has_animation) {
428
status = VP8_STATUS_UNSUPPORTED_FEATURE;
429
}
430
}
431
return status;
432
}
433
434
//------------------------------------------------------------------------------
435
// WebPDecParams
436
437
void WebPResetDecParams(WebPDecParams* const params) {
438
if (params != NULL) {
439
memset(params, 0, sizeof(*params));
440
}
441
}
442
443
//------------------------------------------------------------------------------
444
// "Into" decoding variants
445
446
// Main flow
447
static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
448
WebPDecParams* const params) {
449
VP8StatusCode status;
450
VP8Io io;
451
WebPHeaderStructure headers;
452
453
headers.data = data;
454
headers.data_size = data_size;
455
headers.have_all_data = 1;
456
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
457
if (status != VP8_STATUS_OK) {
458
return status;
459
}
460
461
assert(params != NULL);
462
VP8InitIo(&io);
463
io.data = headers.data + headers.offset;
464
io.data_size = headers.data_size - headers.offset;
465
WebPInitCustomIo(params, &io); // Plug the I/O functions.
466
467
if (!headers.is_lossless) {
468
VP8Decoder* const dec = VP8New();
469
if (dec == NULL) {
470
return VP8_STATUS_OUT_OF_MEMORY;
471
}
472
dec->alpha_data_ = headers.alpha_data;
473
dec->alpha_data_size_ = headers.alpha_data_size;
474
475
// Decode bitstream header, update io->width/io->height.
476
if (!VP8GetHeaders(dec, &io)) {
477
status = dec->status_; // An error occurred. Grab error status.
478
} else {
479
// Allocate/check output buffers.
480
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
481
params->output);
482
if (status == VP8_STATUS_OK) { // Decode
483
// This change must be done before calling VP8Decode()
484
dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
485
io.width, io.height);
486
VP8InitDithering(params->options, dec);
487
if (!VP8Decode(dec, &io)) {
488
status = dec->status_;
489
}
490
}
491
}
492
VP8Delete(dec);
493
} else {
494
VP8LDecoder* const dec = VP8LNew();
495
if (dec == NULL) {
496
return VP8_STATUS_OUT_OF_MEMORY;
497
}
498
if (!VP8LDecodeHeader(dec, &io)) {
499
status = dec->status_; // An error occurred. Grab error status.
500
} else {
501
// Allocate/check output buffers.
502
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
503
params->output);
504
if (status == VP8_STATUS_OK) { // Decode
505
if (!VP8LDecodeImage(dec)) {
506
status = dec->status_;
507
}
508
}
509
}
510
VP8LDelete(dec);
511
}
512
513
if (status != VP8_STATUS_OK) {
514
WebPFreeDecBuffer(params->output);
515
} else {
516
if (params->options != NULL && params->options->flip) {
517
// This restores the original stride values if options->flip was used
518
// during the call to WebPAllocateDecBuffer above.
519
status = WebPFlipBuffer(params->output);
520
}
521
}
522
return status;
523
}
524
525
// Helpers
526
static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
527
const uint8_t* const data,
528
size_t data_size,
529
uint8_t* const rgba,
530
int stride, size_t size) {
531
WebPDecParams params;
532
WebPDecBuffer buf;
533
if (rgba == NULL) {
534
return NULL;
535
}
536
WebPInitDecBuffer(&buf);
537
WebPResetDecParams(&params);
538
params.output = &buf;
539
buf.colorspace = colorspace;
540
buf.u.RGBA.rgba = rgba;
541
buf.u.RGBA.stride = stride;
542
buf.u.RGBA.size = size;
543
buf.is_external_memory = 1;
544
if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
545
return NULL;
546
}
547
return rgba;
548
}
549
550
uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
551
uint8_t* output, size_t size, int stride) {
552
return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
553
}
554
555
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
556
uint8_t* output, size_t size, int stride) {
557
return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
558
}
559
560
uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
561
uint8_t* output, size_t size, int stride) {
562
return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
563
}
564
565
uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
566
uint8_t* output, size_t size, int stride) {
567
return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
568
}
569
570
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
571
uint8_t* output, size_t size, int stride) {
572
return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
573
}
574
575
uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
576
uint8_t* luma, size_t luma_size, int luma_stride,
577
uint8_t* u, size_t u_size, int u_stride,
578
uint8_t* v, size_t v_size, int v_stride) {
579
WebPDecParams params;
580
WebPDecBuffer output;
581
if (luma == NULL) return NULL;
582
WebPInitDecBuffer(&output);
583
WebPResetDecParams(&params);
584
params.output = &output;
585
output.colorspace = MODE_YUV;
586
output.u.YUVA.y = luma;
587
output.u.YUVA.y_stride = luma_stride;
588
output.u.YUVA.y_size = luma_size;
589
output.u.YUVA.u = u;
590
output.u.YUVA.u_stride = u_stride;
591
output.u.YUVA.u_size = u_size;
592
output.u.YUVA.v = v;
593
output.u.YUVA.v_stride = v_stride;
594
output.u.YUVA.v_size = v_size;
595
output.is_external_memory = 1;
596
if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
597
return NULL;
598
}
599
return luma;
600
}
601
602
//------------------------------------------------------------------------------
603
604
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
605
size_t data_size, int* const width, int* const height,
606
WebPDecBuffer* const keep_info) {
607
WebPDecParams params;
608
WebPDecBuffer output;
609
610
WebPInitDecBuffer(&output);
611
WebPResetDecParams(&params);
612
params.output = &output;
613
output.colorspace = mode;
614
615
// Retrieve (and report back) the required dimensions from bitstream.
616
if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
617
return NULL;
618
}
619
if (width != NULL) *width = output.width;
620
if (height != NULL) *height = output.height;
621
622
// Decode
623
if (DecodeInto(data, data_size, &params) != VP8_STATUS_OK) {
624
return NULL;
625
}
626
if (keep_info != NULL) { // keep track of the side-info
627
WebPCopyDecBuffer(&output, keep_info);
628
}
629
// return decoded samples (don't clear 'output'!)
630
return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
631
}
632
633
uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
634
int* width, int* height) {
635
return Decode(MODE_RGB, data, data_size, width, height, NULL);
636
}
637
638
uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
639
int* width, int* height) {
640
return Decode(MODE_RGBA, data, data_size, width, height, NULL);
641
}
642
643
uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
644
int* width, int* height) {
645
return Decode(MODE_ARGB, data, data_size, width, height, NULL);
646
}
647
648
uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
649
int* width, int* height) {
650
return Decode(MODE_BGR, data, data_size, width, height, NULL);
651
}
652
653
uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
654
int* width, int* height) {
655
return Decode(MODE_BGRA, data, data_size, width, height, NULL);
656
}
657
658
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
659
int* width, int* height, uint8_t** u, uint8_t** v,
660
int* stride, int* uv_stride) {
661
WebPDecBuffer output; // only to preserve the side-infos
662
uint8_t* const out = Decode(MODE_YUV, data, data_size,
663
width, height, &output);
664
665
if (out != NULL) {
666
const WebPYUVABuffer* const buf = &output.u.YUVA;
667
*u = buf->u;
668
*v = buf->v;
669
*stride = buf->y_stride;
670
*uv_stride = buf->u_stride;
671
assert(buf->u_stride == buf->v_stride);
672
}
673
return out;
674
}
675
676
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
677
assert(features != NULL);
678
memset(features, 0, sizeof(*features));
679
}
680
681
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
682
WebPBitstreamFeatures* const features) {
683
if (features == NULL || data == NULL) {
684
return VP8_STATUS_INVALID_PARAM;
685
}
686
DefaultFeatures(features);
687
688
// Only parse enough of the data to retrieve the features.
689
return ParseHeadersInternal(data, data_size,
690
&features->width, &features->height,
691
&features->has_alpha, &features->has_animation,
692
&features->format, NULL);
693
}
694
695
//------------------------------------------------------------------------------
696
// WebPGetInfo()
697
698
int WebPGetInfo(const uint8_t* data, size_t data_size,
699
int* width, int* height) {
700
WebPBitstreamFeatures features;
701
702
if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
703
return 0;
704
}
705
706
if (width != NULL) {
707
*width = features.width;
708
}
709
if (height != NULL) {
710
*height = features.height;
711
}
712
713
return 1;
714
}
715
716
//------------------------------------------------------------------------------
717
// Advance decoding API
718
719
int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
720
int version) {
721
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
722
return 0; // version mismatch
723
}
724
if (config == NULL) {
725
return 0;
726
}
727
memset(config, 0, sizeof(*config));
728
DefaultFeatures(&config->input);
729
WebPInitDecBuffer(&config->output);
730
return 1;
731
}
732
733
VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
734
WebPBitstreamFeatures* features,
735
int version) {
736
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
737
return VP8_STATUS_INVALID_PARAM; // version mismatch
738
}
739
if (features == NULL) {
740
return VP8_STATUS_INVALID_PARAM;
741
}
742
return GetFeatures(data, data_size, features);
743
}
744
745
VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
746
WebPDecoderConfig* config) {
747
WebPDecParams params;
748
VP8StatusCode status;
749
750
if (config == NULL) {
751
return VP8_STATUS_INVALID_PARAM;
752
}
753
754
status = GetFeatures(data, data_size, &config->input);
755
if (status != VP8_STATUS_OK) {
756
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
757
return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
758
}
759
return status;
760
}
761
762
WebPResetDecParams(&params);
763
params.options = &config->options;
764
params.output = &config->output;
765
if (WebPAvoidSlowMemory(params.output, &config->input)) {
766
// decoding to slow memory: use a temporary in-mem buffer to decode into.
767
WebPDecBuffer in_mem_buffer;
768
WebPInitDecBuffer(&in_mem_buffer);
769
in_mem_buffer.colorspace = config->output.colorspace;
770
in_mem_buffer.width = config->input.width;
771
in_mem_buffer.height = config->input.height;
772
params.output = &in_mem_buffer;
773
status = DecodeInto(data, data_size, &params);
774
if (status == VP8_STATUS_OK) { // do the slow-copy
775
status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output);
776
}
777
WebPFreeDecBuffer(&in_mem_buffer);
778
} else {
779
status = DecodeInto(data, data_size, &params);
780
}
781
782
return status;
783
}
784
785
//------------------------------------------------------------------------------
786
// Cropping and rescaling.
787
788
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
789
VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
790
const int W = io->width;
791
const int H = io->height;
792
int x = 0, y = 0, w = W, h = H;
793
794
// Cropping
795
io->use_cropping = (options != NULL) && (options->use_cropping > 0);
796
if (io->use_cropping) {
797
w = options->crop_width;
798
h = options->crop_height;
799
x = options->crop_left;
800
y = options->crop_top;
801
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
802
x &= ~1;
803
y &= ~1;
804
}
805
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
806
return 0; // out of frame boundary error
807
}
808
}
809
io->crop_left = x;
810
io->crop_top = y;
811
io->crop_right = x + w;
812
io->crop_bottom = y + h;
813
io->mb_w = w;
814
io->mb_h = h;
815
816
// Scaling
817
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
818
if (io->use_scaling) {
819
int scaled_width = options->scaled_width;
820
int scaled_height = options->scaled_height;
821
if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) {
822
return 0;
823
}
824
io->scaled_width = scaled_width;
825
io->scaled_height = scaled_height;
826
}
827
828
// Filter
829
io->bypass_filtering = (options != NULL) && options->bypass_filtering;
830
831
// Fancy upsampler
832
#ifdef FANCY_UPSAMPLING
833
io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
834
#endif
835
836
if (io->use_scaling) {
837
// disable filter (only for large downscaling ratio).
838
io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
839
(io->scaled_height < H * 3 / 4);
840
io->fancy_upsampling = 0;
841
}
842
return 1;
843
}
844
845
//------------------------------------------------------------------------------
846
847