xref: /aosp_15_r20/external/webp/src/dec/webp_dec.c (revision b2055c353e87c8814eb2b6b1b11112a1562253bd)
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(&params);
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, &params) != 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(&params);
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, &params) != 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(&params);
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, &params) != 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(&params);
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, &params);
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, &params);
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