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