Bug Summary

File:root/firefox-clang/media/libwebp/src/dec/idec_dec.c
Warning:line 235, column 39
Subtraction of a non-null pointer and a null pointer (from variable 'old_start') results in undefined behavior

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name idec_dec.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libwebp/src/dec -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libwebp/src/dec -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WEBP_USE_THREAD=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/media/libwebp/src/dec -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/media/libwebp/src/dec -I /root/firefox-clang/media/libwebp -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=tautological-type-limit-compare -Wno-range-loop-analysis -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-unknown-warning-option -Wno-character-conversion -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c /root/firefox-clang/media/libwebp/src/dec/idec_dec.c
1// Copyright 2011 Google Inc. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8// -----------------------------------------------------------------------------
9//
10// Incremental decoding
11//
12// Author: somnath@google.com (Somnath Banerjee)
13
14#include <assert.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include "src/dec/alphai_dec.h"
19#include "src/dec/vp8_dec.h"
20#include "src/dec/vp8i_dec.h"
21#include "src/dec/vp8li_dec.h"
22#include "src/dec/webpi_dec.h"
23#include "src/utils/bit_reader_utils.h"
24#include "src/utils/thread_utils.h"
25#include "src/utils/utils.h"
26#include "src/webp/decode.h"
27#include "src/webp/format_constants.h"
28#include "src/webp/types.h"
29
30// In append mode, buffer allocations increase as multiples of this value.
31// Needs to be a power of 2.
32#define CHUNK_SIZE4096 4096
33#define MAX_MB_SIZE4096 4096
34
35//------------------------------------------------------------------------------
36// Data structures for memory and states
37
38// Decoding states. State normally flows as:
39// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
40// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
41// If there is any error the decoder goes into state ERROR.
42typedef enum {
43 STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
44 STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
45 STATE_VP8_PARTS0,
46 STATE_VP8_DATA,
47 STATE_VP8L_HEADER,
48 STATE_VP8L_DATA,
49 STATE_DONE,
50 STATE_ERROR
51} DecState;
52
53// Operating state for the MemBuffer
54typedef enum {
55 MEM_MODE_NONE = 0,
56 MEM_MODE_APPEND,
57 MEM_MODE_MAP
58} MemBufferMode;
59
60// storage for partition #0 and partial data (in a rolling fashion)
61typedef struct {
62 MemBufferMode mode; // Operation mode
63 size_t start; // start location of the data to be decoded
64 size_t end; // end location
65 size_t buf_size; // size of the allocated buffer
66 uint8_t* buf; // We don't own this buffer in case WebPIUpdate()
67
68 size_t part0_size; // size of partition #0
69 const uint8_t* part0_buf; // buffer to store partition #0
70} MemBuffer;
71
72struct WebPIDecoder {
73 DecState state; // current decoding state
74 WebPDecParams params; // Params to store output info
75 int is_lossless; // for down-casting 'dec'.
76 void* dec; // either a VP8Decoder or a VP8LDecoder instance
77 VP8Io io;
78
79 MemBuffer mem; // input memory buffer.
80 WebPDecBuffer output; // output buffer (when no external one is supplied,
81 // or if the external one has slow-memory)
82 WebPDecBuffer* final_output; // Slow-memory output to copy to eventually.
83 size_t chunk_size; // Compressed VP8/VP8L size extracted from Header.
84
85 int last_mb_y; // last row reached for intra-mode decoding
86};
87
88// MB context to restore in case VP8DecodeMB() fails
89typedef struct {
90 VP8MB left;
91 VP8MB info;
92 VP8BitReader token_br;
93} MBContext;
94
95//------------------------------------------------------------------------------
96// MemBuffer: incoming data handling
97
98static WEBP_INLINEinline size_t MemDataSize(const MemBuffer* mem) {
99 return (mem->end - mem->start);
100}
101
102// Check if we need to preserve the compressed alpha data, as it may not have
103// been decoded yet.
104static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
105 if (idec->state == STATE_WEBP_HEADER) {
106 // We haven't parsed the headers yet, so we don't know whether the image is
107 // lossy or lossless. This also means that we haven't parsed the ALPH chunk.
108 return 0;
109 }
110 if (idec->is_lossless) {
111 return 0; // ALPH chunk is not present for lossless images.
112 } else {
113 const VP8Decoder* const dec = (VP8Decoder*)idec->dec;
114 assert(dec != NULL)((void) sizeof ((dec != ((void*)0)) ? 1 : 0), __extension__ (
{ if (dec != ((void*)0)) ; else __assert_fail ("dec != NULL",
"/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 114,
__extension__ __PRETTY_FUNCTION__); }))
; // Must be true as idec->state != STATE_WEBP_HEADER.
115 return (dec->alpha_data != NULL((void*)0)) && !dec->is_alpha_decoded;
116 }
117}
118
119static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
120 MemBuffer* const mem = &idec->mem;
121 const uint8_t* const new_base = mem->buf + mem->start;
122 // note: for VP8, setting up idec->io is only really needed at the beginning
123 // of the decoding, till partition #0 is complete.
124 idec->io.data = new_base;
125 idec->io.data_size = MemDataSize(mem);
126
127 if (idec->dec != NULL((void*)0)) {
128 if (!idec->is_lossless) {
129 VP8Decoder* const dec = (VP8Decoder*)idec->dec;
130 const uint32_t last_part = dec->num_parts_minus_one;
131 if (offset != 0) {
132 uint32_t p;
133 for (p = 0; p <= last_part; ++p) {
134 VP8RemapBitReader(dec->parts + p, offset);
135 }
136 // Remap partition #0 data pointer to new offset, but only in MAP
137 // mode (in APPEND mode, partition #0 is copied into a fixed memory).
138 if (mem->mode == MEM_MODE_MAP) {
139 VP8RemapBitReader(&dec->br, offset);
140 }
141 }
142 {
143 const uint8_t* const last_start = dec->parts[last_part].buf;
144 // 'last_start' will be NULL when 'idec->state' is < STATE_VP8_PARTS0
145 // and through a portion of that state (when there isn't enough data to
146 // parse the partitions). The bitreader is only used meaningfully when
147 // there is enough data to begin parsing partition 0.
148 if (last_start != NULL((void*)0)) {
149 VP8BitReaderSetBuffer(&dec->parts[last_part], last_start,
150 mem->buf + mem->end - last_start);
151 }
152 }
153 if (NeedCompressedAlpha(idec)) {
154 ALPHDecoder* const alph_dec = dec->alph_dec;
155 dec->alpha_data += offset;
156 if (alph_dec != NULL((void*)0) && alph_dec->vp8l_dec != NULL((void*)0)) {
157 if (alph_dec->method == ALPHA_LOSSLESS_COMPRESSION1) {
158 VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec;
159 assert(dec->alpha_data_size >= ALPHA_HEADER_LEN)((void) sizeof ((dec->alpha_data_size >= 1) ? 1 : 0), __extension__
({ if (dec->alpha_data_size >= 1) ; else __assert_fail
("dec->alpha_data_size >= ALPHA_HEADER_LEN", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 159, __extension__ __PRETTY_FUNCTION__); }))
;
160 VP8LBitReaderSetBuffer(&alph_vp8l_dec->br,
161 dec->alpha_data + ALPHA_HEADER_LEN1,
162 dec->alpha_data_size - ALPHA_HEADER_LEN1);
163 } else { // alph_dec->method == ALPHA_NO_COMPRESSION
164 // Nothing special to do in this case.
165 }
166 }
167 }
168 } else { // Resize lossless bitreader
169 VP8LDecoder* const dec = (VP8LDecoder*)idec->dec;
170 VP8LBitReaderSetBuffer(&dec->br, new_base, MemDataSize(mem));
171 }
172 }
173}
174
175// Appends data to the end of MemBuffer->buf. It expands the allocated memory
176// size if required and also updates VP8BitReader's if new memory is allocated.
177WEBP_NODISCARD static int AppendToMemBuffer(WebPIDecoder* const idec,
178 const uint8_t* const data,
179 size_t data_size) {
180 VP8Decoder* const dec = (VP8Decoder*)idec->dec;
181 MemBuffer* const mem = &idec->mem;
182 const int need_compressed_alpha = NeedCompressedAlpha(idec);
183 const uint8_t* const old_start =
184 (mem->buf == NULL((void*)0)) ? NULL((void*)0) : mem->buf + mem->start;
185 const uint8_t* const old_base =
186 need_compressed_alpha ? dec->alpha_data : old_start;
187 assert(mem->buf != NULL || mem->start == 0)((void) sizeof ((mem->buf != ((void*)0) || mem->start ==
0) ? 1 : 0), __extension__ ({ if (mem->buf != ((void*)0) ||
mem->start == 0) ; else __assert_fail ("mem->buf != NULL || mem->start == 0"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 187
, __extension__ __PRETTY_FUNCTION__); }))
;
188 assert(mem->mode == MEM_MODE_APPEND)((void) sizeof ((mem->mode == MEM_MODE_APPEND) ? 1 : 0), __extension__
({ if (mem->mode == MEM_MODE_APPEND) ; else __assert_fail
("mem->mode == MEM_MODE_APPEND", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 188, __extension__ __PRETTY_FUNCTION__); }))
;
189 if (data_size > MAX_CHUNK_PAYLOAD(~0U - 8 - 1)) {
190 // security safeguard: trying to allocate more than what the format
191 // allows for a chunk should be considered a smoke smell.
192 return 0;
193 }
194
195 if (mem->end + data_size > mem->buf_size) { // Need some free memory
196 const size_t new_mem_start = old_start - old_base;
197 const size_t current_size = MemDataSize(mem) + new_mem_start;
198 const uint64_t new_size = (uint64_t)current_size + data_size;
199 const uint64_t extra_size = (new_size + CHUNK_SIZE4096 - 1) & ~(CHUNK_SIZE4096 - 1);
200 uint8_t* const new_buf =
201 (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
202 if (new_buf == NULL((void*)0)) return 0;
203 if (old_base != NULL((void*)0)) memcpy(new_buf, old_base, current_size);
204 WebPSafeFree(mem->buf);
205 mem->buf = new_buf;
206 mem->buf_size = (size_t)extra_size;
207 mem->start = new_mem_start;
208 mem->end = current_size;
209 }
210
211 assert(mem->buf != NULL)((void) sizeof ((mem->buf != ((void*)0)) ? 1 : 0), __extension__
({ if (mem->buf != ((void*)0)) ; else __assert_fail ("mem->buf != NULL"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 211
, __extension__ __PRETTY_FUNCTION__); }))
;
212 memcpy(mem->buf + mem->end, data, data_size);
213 mem->end += data_size;
214 assert(mem->end <= mem->buf_size)((void) sizeof ((mem->end <= mem->buf_size) ? 1 : 0)
, __extension__ ({ if (mem->end <= mem->buf_size) ; else
__assert_fail ("mem->end <= mem->buf_size", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 214, __extension__ __PRETTY_FUNCTION__); }))
;
215
216 DoRemap(idec, mem->buf + mem->start - old_start);
217 return 1;
218}
219
220WEBP_NODISCARD static int RemapMemBuffer(WebPIDecoder* const idec,
221 const uint8_t* const data,
222 size_t data_size) {
223 MemBuffer* const mem = &idec->mem;
224 const uint8_t* const old_buf = mem->buf;
225 const uint8_t* const old_start =
9
'old_start' initialized to a null pointer value
226 (old_buf == NULL((void*)0)) ? NULL((void*)0) : old_buf + mem->start;
7
Assuming 'old_buf' is equal to NULL
8
'?' condition is true
227 assert(old_buf != NULL || mem->start == 0)((void) sizeof ((old_buf != ((void*)0) || mem->start == 0)
? 1 : 0), __extension__ ({ if (old_buf != ((void*)0) || mem->
start == 0) ; else __assert_fail ("old_buf != NULL || mem->start == 0"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 227
, __extension__ __PRETTY_FUNCTION__); }))
;
10
Assuming field 'start' is equal to 0
11
Taking true branch
228 assert(mem->mode == MEM_MODE_MAP)((void) sizeof ((mem->mode == MEM_MODE_MAP) ? 1 : 0), __extension__
({ if (mem->mode == MEM_MODE_MAP) ; else __assert_fail ("mem->mode == MEM_MODE_MAP"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 228
, __extension__ __PRETTY_FUNCTION__); }))
;
12
Taking true branch
229
230 if (data_size < mem->buf_size) return 0; // can't remap to a shorter buffer!
13
Assuming 'data_size' is >= field 'buf_size'
14
Taking false branch
231
232 mem->buf = (uint8_t*)data;
233 mem->end = mem->buf_size = data_size;
234
235 DoRemap(idec, mem->buf + mem->start - old_start);
15
Subtraction of a non-null pointer and a null pointer (from variable 'old_start') results in undefined behavior
236 return 1;
237}
238
239static void InitMemBuffer(MemBuffer* const mem) {
240 mem->mode = MEM_MODE_NONE;
241 mem->buf = NULL((void*)0);
242 mem->buf_size = 0;
243 mem->part0_buf = NULL((void*)0);
244 mem->part0_size = 0;
245}
246
247static void ClearMemBuffer(MemBuffer* const mem) {
248 assert(mem)((void) sizeof ((mem) ? 1 : 0), __extension__ ({ if (mem) ; else
__assert_fail ("mem", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 248, __extension__ __PRETTY_FUNCTION__); }))
;
249 if (mem->mode == MEM_MODE_APPEND) {
250 WebPSafeFree(mem->buf);
251 WebPSafeFree((void*)mem->part0_buf);
252 }
253}
254
255WEBP_NODISCARD static int CheckMemBufferMode(MemBuffer* const mem,
256 MemBufferMode expected) {
257 if (mem->mode == MEM_MODE_NONE) {
258 mem->mode = expected; // switch to the expected mode
259 } else if (mem->mode != expected) {
260 return 0; // we mixed the modes => error
261 }
262 assert(mem->mode == expected)((void) sizeof ((mem->mode == expected) ? 1 : 0), __extension__
({ if (mem->mode == expected) ; else __assert_fail ("mem->mode == expected"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 262
, __extension__ __PRETTY_FUNCTION__); }))
; // mode is ok
263 return 1;
264}
265
266// To be called last.
267WEBP_NODISCARD static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
268 const WebPDecoderOptions* const options = idec->params.options;
269 WebPDecBuffer* const output = idec->params.output;
270
271 idec->state = STATE_DONE;
272 if (options != NULL((void*)0) && options->flip) {
273 const VP8StatusCode status = WebPFlipBuffer(output);
274 if (status != VP8_STATUS_OK) return status;
275 }
276 if (idec->final_output != NULL((void*)0)) {
277 const VP8StatusCode status = WebPCopyDecBufferPixels(
278 output, idec->final_output); // do the slow-copy
279 WebPFreeDecBuffer(&idec->output);
280 if (status != VP8_STATUS_OK) return status;
281 *output = *idec->final_output;
282 idec->final_output = NULL((void*)0);
283 }
284 return VP8_STATUS_OK;
285}
286
287//------------------------------------------------------------------------------
288// Macroblock-decoding contexts
289
290static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
291 MBContext* const context) {
292 context->left = dec->mb_info[-1];
293 context->info = dec->mb_info[dec->mb_x];
294 context->token_br = *token_br;
295}
296
297static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
298 VP8BitReader* const token_br) {
299 dec->mb_info[-1] = context->left;
300 dec->mb_info[dec->mb_x] = context->info;
301 *token_br = context->token_br;
302}
303
304//------------------------------------------------------------------------------
305
306static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
307 if (idec->state == STATE_VP8_DATA) {
308 // Synchronize the thread, clean-up and check for errors.
309 (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io);
310 }
311 idec->state = STATE_ERROR;
312 return error;
313}
314
315static void ChangeState(WebPIDecoder* const idec, DecState new_state,
316 size_t consumed_bytes) {
317 MemBuffer* const mem = &idec->mem;
318 idec->state = new_state;
319 mem->start += consumed_bytes;
320 assert(mem->start <= mem->end)((void) sizeof ((mem->start <= mem->end) ? 1 : 0), __extension__
({ if (mem->start <= mem->end) ; else __assert_fail
("mem->start <= mem->end", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 320, __extension__ __PRETTY_FUNCTION__); }))
;
321 idec->io.data = mem->buf + mem->start;
322 idec->io.data_size = MemDataSize(mem);
323}
324
325// Headers
326static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
327 MemBuffer* const mem = &idec->mem;
328 const uint8_t* data = mem->buf + mem->start;
329 size_t curr_size = MemDataSize(mem);
330 VP8StatusCode status;
331 WebPHeaderStructure headers;
332
333 headers.data = data;
334 headers.data_size = curr_size;
335 headers.have_all_data = 0;
336 status = WebPParseHeaders(&headers);
337 if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
338 return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
339 } else if (status != VP8_STATUS_OK) {
340 return IDecError(idec, status);
341 }
342
343 idec->chunk_size = headers.compressed_size;
344 idec->is_lossless = headers.is_lossless;
345 if (!idec->is_lossless) {
346 VP8Decoder* const dec = VP8New();
347 if (dec == NULL((void*)0)) {
348 return VP8_STATUS_OUT_OF_MEMORY;
349 }
350 dec->incremental = 1;
351 idec->dec = dec;
352 dec->alpha_data = headers.alpha_data;
353 dec->alpha_data_size = headers.alpha_data_size;
354 ChangeState(idec, STATE_VP8_HEADER, headers.offset);
355 } else {
356 VP8LDecoder* const dec = VP8LNew();
357 if (dec == NULL((void*)0)) {
358 return VP8_STATUS_OUT_OF_MEMORY;
359 }
360 idec->dec = dec;
361 ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
362 }
363 return VP8_STATUS_OK;
364}
365
366static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
367 const uint8_t* data = idec->mem.buf + idec->mem.start;
368 const size_t curr_size = MemDataSize(&idec->mem);
369 int width, height;
370 uint32_t bits;
371
372 if (curr_size < VP8_FRAME_HEADER_SIZE10) {
373 // Not enough data bytes to extract VP8 Frame Header.
374 return VP8_STATUS_SUSPENDED;
375 }
376 if (!VP8GetInfo(data, curr_size, idec->chunk_size, &width, &height)) {
377 return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
378 }
379
380 bits = data[0] | (data[1] << 8) | (data[2] << 16);
381 idec->mem.part0_size = (bits >> 5) + VP8_FRAME_HEADER_SIZE10;
382
383 idec->io.data = data;
384 idec->io.data_size = curr_size;
385 idec->state = STATE_VP8_PARTS0;
386 return VP8_STATUS_OK;
387}
388
389// Partition #0
390static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
391 VP8Decoder* const dec = (VP8Decoder*)idec->dec;
392 VP8BitReader* const br = &dec->br;
393 const size_t part_size = br->buf_end - br->buf;
394 MemBuffer* const mem = &idec->mem;
395 assert(!idec->is_lossless)((void) sizeof ((!idec->is_lossless) ? 1 : 0), __extension__
({ if (!idec->is_lossless) ; else __assert_fail ("!idec->is_lossless"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 395
, __extension__ __PRETTY_FUNCTION__); }))
;
396 assert(mem->part0_buf == NULL)((void) sizeof ((mem->part0_buf == ((void*)0)) ? 1 : 0), __extension__
({ if (mem->part0_buf == ((void*)0)) ; else __assert_fail
("mem->part0_buf == NULL", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 396, __extension__ __PRETTY_FUNCTION__); }))
;
397 // the following is a format limitation, no need for runtime check:
398 assert(part_size <= mem->part0_size)((void) sizeof ((part_size <= mem->part0_size) ? 1 : 0)
, __extension__ ({ if (part_size <= mem->part0_size) ; else
__assert_fail ("part_size <= mem->part0_size", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 398, __extension__ __PRETTY_FUNCTION__); }))
;
399 if (part_size == 0) { // can't have zero-size partition #0
400 return VP8_STATUS_BITSTREAM_ERROR;
401 }
402 if (mem->mode == MEM_MODE_APPEND) {
403 // We copy and grab ownership of the partition #0 data.
404 uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
405 if (part0_buf == NULL((void*)0)) {
406 return VP8_STATUS_OUT_OF_MEMORY;
407 }
408 memcpy(part0_buf, br->buf, part_size);
409 mem->part0_buf = part0_buf;
410 VP8BitReaderSetBuffer(br, part0_buf, part_size);
411 } else {
412 // Else: just keep pointers to the partition #0's data in dec->br.
413 }
414 mem->start += part_size;
415 return VP8_STATUS_OK;
416}
417
418static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
419 VP8Decoder* const dec = (VP8Decoder*)idec->dec;
420 VP8Io* const io = &idec->io;
421 const WebPDecParams* const params = &idec->params;
422 WebPDecBuffer* const output = params->output;
423
424 // Wait till we have enough data for the whole partition #0
425 if (MemDataSize(&idec->mem) < idec->mem.part0_size) {
426 return VP8_STATUS_SUSPENDED;
427 }
428
429 if (!VP8GetHeaders(dec, io)) {
430 const VP8StatusCode status = dec->status;
431 if (status == VP8_STATUS_SUSPENDED ||
432 status == VP8_STATUS_NOT_ENOUGH_DATA) {
433 // treating NOT_ENOUGH_DATA as SUSPENDED state
434 return VP8_STATUS_SUSPENDED;
435 }
436 return IDecError(idec, status);
437 }
438
439 // Allocate/Verify output buffer now
440 dec->status = WebPAllocateDecBuffer(io->width, io->height, params->options,
441 output);
442 if (dec->status != VP8_STATUS_OK) {
443 return IDecError(idec, dec->status);
444 }
445 // This change must be done before calling VP8InitFrame()
446 dec->mt_method = VP8GetThreadMethod(params->options, NULL((void*)0),
447 io->width, io->height);
448 VP8InitDithering(params->options, dec);
449
450 dec->status = CopyParts0Data(idec);
451 if (dec->status != VP8_STATUS_OK) {
452 return IDecError(idec, dec->status);
453 }
454
455 // Finish setting up the decoding parameters. Will call io->setup().
456 if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {
457 return IDecError(idec, dec->status);
458 }
459
460 // Note: past this point, teardown() must always be called
461 // in case of error.
462 idec->state = STATE_VP8_DATA;
463 // Allocate memory and prepare everything.
464 if (!VP8InitFrame(dec, io)) {
465 return IDecError(idec, dec->status);
466 }
467 return VP8_STATUS_OK;
468}
469
470// Remaining partitions
471static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
472 VP8Decoder* const dec = (VP8Decoder*)idec->dec;
473 VP8Io* const io = &idec->io;
474
475 // Make sure partition #0 has been read before, to set dec to ready.
476 if (!dec->ready) {
477 return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
478 }
479 for (; dec->mb_y < dec->mb_h; ++dec->mb_y) {
480 if (idec->last_mb_y != dec->mb_y) {
481 if (!VP8ParseIntraModeRow(&dec->br, dec)) {
482 // note: normally, error shouldn't occur since we already have the whole
483 // partition0 available here in DecodeRemaining(). Reaching EOF while
484 // reading intra modes really means a BITSTREAM_ERROR.
485 return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
486 }
487 idec->last_mb_y = dec->mb_y;
488 }
489 for (; dec->mb_x < dec->mb_w; ++dec->mb_x) {
490 VP8BitReader* const token_br =
491 &dec->parts[dec->mb_y & dec->num_parts_minus_one];
492 MBContext context;
493 SaveContext(dec, token_br, &context);
494 if (!VP8DecodeMB(dec, token_br)) {
495 // We shouldn't fail when MAX_MB data was available
496 if (dec->num_parts_minus_one == 0 &&
497 MemDataSize(&idec->mem) > MAX_MB_SIZE4096) {
498 return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
499 }
500 // Synchronize the threads.
501 if (dec->mt_method > 0) {
502 if (!WebPGetWorkerInterface()->Sync(&dec->worker)) {
503 return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
504 }
505 }
506 RestoreContext(&context, dec, token_br);
507 return VP8_STATUS_SUSPENDED;
508 }
509 // Release buffer only if there is only one partition
510 if (dec->num_parts_minus_one == 0) {
511 idec->mem.start = token_br->buf - idec->mem.buf;
512 assert(idec->mem.start <= idec->mem.end)((void) sizeof ((idec->mem.start <= idec->mem.end) ?
1 : 0), __extension__ ({ if (idec->mem.start <= idec->
mem.end) ; else __assert_fail ("idec->mem.start <= idec->mem.end"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 512
, __extension__ __PRETTY_FUNCTION__); }))
;
513 }
514 }
515 VP8InitScanline(dec); // Prepare for next scanline
516
517 // Reconstruct, filter and emit the row.
518 if (!VP8ProcessRow(dec, io)) {
519 return IDecError(idec, VP8_STATUS_USER_ABORT);
520 }
521 }
522 // Synchronize the thread and check for errors.
523 if (!VP8ExitCritical(dec, io)) {
524 idec->state = STATE_ERROR; // prevent re-entry in IDecError
525 return IDecError(idec, VP8_STATUS_USER_ABORT);
526 }
527 dec->ready = 0;
528 return FinishDecoding(idec);
529}
530
531static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,
532 VP8StatusCode status) {
533 if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
534 return VP8_STATUS_SUSPENDED;
535 }
536 return IDecError(idec, status);
537}
538
539static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
540 VP8Io* const io = &idec->io;
541 VP8LDecoder* const dec = (VP8LDecoder*)idec->dec;
542 const WebPDecParams* const params = &idec->params;
543 WebPDecBuffer* const output = params->output;
544 size_t curr_size = MemDataSize(&idec->mem);
545 assert(idec->is_lossless)((void) sizeof ((idec->is_lossless) ? 1 : 0), __extension__
({ if (idec->is_lossless) ; else __assert_fail ("idec->is_lossless"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 545
, __extension__ __PRETTY_FUNCTION__); }))
;
546
547 // Wait until there's enough data for decoding header.
548 if (curr_size < (idec->chunk_size >> 3)) {
549 dec->status = VP8_STATUS_SUSPENDED;
550 return ErrorStatusLossless(idec, dec->status);
551 }
552
553 if (!VP8LDecodeHeader(dec, io)) {
554 if (dec->status == VP8_STATUS_BITSTREAM_ERROR &&
555 curr_size < idec->chunk_size) {
556 dec->status = VP8_STATUS_SUSPENDED;
557 }
558 return ErrorStatusLossless(idec, dec->status);
559 }
560 // Allocate/verify output buffer now.
561 dec->status = WebPAllocateDecBuffer(io->width, io->height, params->options,
562 output);
563 if (dec->status != VP8_STATUS_OK) {
564 return IDecError(idec, dec->status);
565 }
566
567 idec->state = STATE_VP8L_DATA;
568 return VP8_STATUS_OK;
569}
570
571static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
572 VP8LDecoder* const dec = (VP8LDecoder*)idec->dec;
573 const size_t curr_size = MemDataSize(&idec->mem);
574 assert(idec->is_lossless)((void) sizeof ((idec->is_lossless) ? 1 : 0), __extension__
({ if (idec->is_lossless) ; else __assert_fail ("idec->is_lossless"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 574
, __extension__ __PRETTY_FUNCTION__); }))
;
575
576 // Switch to incremental decoding if we don't have all the bytes available.
577 dec->incremental = (curr_size < idec->chunk_size);
578
579 if (!VP8LDecodeImage(dec)) {
580 return ErrorStatusLossless(idec, dec->status);
581 }
582 assert(dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED)((void) sizeof ((dec->status == VP8_STATUS_OK || dec->status
== VP8_STATUS_SUSPENDED) ? 1 : 0), __extension__ ({ if (dec->
status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED
) ; else __assert_fail ("dec->status == VP8_STATUS_OK || dec->status == VP8_STATUS_SUSPENDED"
, "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c", 582
, __extension__ __PRETTY_FUNCTION__); }))
;
583 return (dec->status == VP8_STATUS_SUSPENDED) ? dec->status
584 : FinishDecoding(idec);
585}
586
587 // Main decoding loop
588static VP8StatusCode IDecode(WebPIDecoder* idec) {
589 VP8StatusCode status = VP8_STATUS_SUSPENDED;
590
591 if (idec->state == STATE_WEBP_HEADER) {
592 status = DecodeWebPHeaders(idec);
593 } else {
594 if (idec->dec == NULL((void*)0)) {
595 return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
596 }
597 }
598 if (idec->state == STATE_VP8_HEADER) {
599 status = DecodeVP8FrameHeader(idec);
600 }
601 if (idec->state == STATE_VP8_PARTS0) {
602 status = DecodePartition0(idec);
603 }
604 if (idec->state == STATE_VP8_DATA) {
605 const VP8Decoder* const dec = (VP8Decoder*)idec->dec;
606 if (dec == NULL((void*)0)) {
607 return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
608 }
609 status = DecodeRemaining(idec);
610 }
611 if (idec->state == STATE_VP8L_HEADER) {
612 status = DecodeVP8LHeader(idec);
613 }
614 if (idec->state == STATE_VP8L_DATA) {
615 status = DecodeVP8LData(idec);
616 }
617 return status;
618}
619
620//------------------------------------------------------------------------------
621// Internal constructor
622
623WEBP_NODISCARD static WebPIDecoder* NewDecoder(
624 WebPDecBuffer* const output_buffer,
625 const WebPBitstreamFeatures* const features) {
626 WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
627 if (idec == NULL((void*)0)) {
628 return NULL((void*)0);
629 }
630
631 idec->state = STATE_WEBP_HEADER;
632 idec->chunk_size = 0;
633
634 idec->last_mb_y = -1;
635
636 InitMemBuffer(&idec->mem);
637 if (!WebPInitDecBuffer(&idec->output) || !VP8InitIo(&idec->io)) {
638 WebPSafeFree(idec);
639 return NULL((void*)0);
640 }
641
642 WebPResetDecParams(&idec->params);
643 if (output_buffer == NULL((void*)0) || WebPAvoidSlowMemory(output_buffer, features)) {
644 idec->params.output = &idec->output;
645 idec->final_output = output_buffer;
646 if (output_buffer != NULL((void*)0)) {
647 idec->params.output->colorspace = output_buffer->colorspace;
648 }
649 } else {
650 idec->params.output = output_buffer;
651 idec->final_output = NULL((void*)0);
652 }
653 WebPInitCustomIo(&idec->params, &idec->io); // Plug the I/O functions.
654
655 return idec;
656}
657
658//------------------------------------------------------------------------------
659// Public functions
660
661WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
662 return NewDecoder(output_buffer, NULL((void*)0));
663}
664
665WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
666 WebPDecoderConfig* config) {
667 WebPIDecoder* idec;
668 WebPBitstreamFeatures tmp_features;
669 WebPBitstreamFeatures* const features =
670 (config == NULL((void*)0)) ? &tmp_features : &config->input;
671 memset(&tmp_features, 0, sizeof(tmp_features));
672
673 // Parse the bitstream's features, if requested:
674 if (data != NULL((void*)0) && data_size > 0) {
675 if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
676 return NULL((void*)0);
677 }
678 }
679
680 // Create an instance of the incremental decoder
681 idec = (config != NULL((void*)0)) ? NewDecoder(&config->output, features)
682 : NewDecoder(NULL((void*)0), features);
683 if (idec == NULL((void*)0)) {
684 return NULL((void*)0);
685 }
686 // Finish initialization
687 if (config != NULL((void*)0)) {
688 idec->params.options = &config->options;
689 }
690 return idec;
691}
692
693void WebPIDelete(WebPIDecoder* idec) {
694 if (idec == NULL((void*)0)) return;
695 if (idec->dec != NULL((void*)0)) {
696 if (!idec->is_lossless) {
697 if (idec->state == STATE_VP8_DATA) {
698 // Synchronize the thread, clean-up and check for errors.
699 // TODO(vrabaud) do we care about the return result?
700 (void)VP8ExitCritical((VP8Decoder*)idec->dec, &idec->io);
701 }
702 VP8Delete((VP8Decoder*)idec->dec);
703 } else {
704 VP8LDelete((VP8LDecoder*)idec->dec);
705 }
706 }
707 ClearMemBuffer(&idec->mem);
708 WebPFreeDecBuffer(&idec->output);
709 WebPSafeFree(idec);
710}
711
712//------------------------------------------------------------------------------
713// Wrapper toward WebPINewDecoder
714
715WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer,
716 size_t output_buffer_size, int output_stride) {
717 const int is_external_memory = (output_buffer != NULL((void*)0)) ? 1 : 0;
718 WebPIDecoder* idec;
719
720 if (csp >= MODE_YUV) return NULL((void*)0);
721 if (is_external_memory == 0) { // Overwrite parameters to sane values.
722 output_buffer_size = 0;
723 output_stride = 0;
724 } else { // A buffer was passed. Validate the other params.
725 if (output_stride == 0 || output_buffer_size == 0) {
726 return NULL((void*)0); // invalid parameter.
727 }
728 }
729 idec = WebPINewDecoder(NULL((void*)0));
730 if (idec == NULL((void*)0)) return NULL((void*)0);
731 idec->output.colorspace = csp;
732 idec->output.is_external_memory = is_external_memory;
733 idec->output.u.RGBA.rgba = output_buffer;
734 idec->output.u.RGBA.stride = output_stride;
735 idec->output.u.RGBA.size = output_buffer_size;
736 return idec;
737}
738
739WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
740 uint8_t* u, size_t u_size, int u_stride,
741 uint8_t* v, size_t v_size, int v_stride,
742 uint8_t* a, size_t a_size, int a_stride) {
743 const int is_external_memory = (luma != NULL((void*)0)) ? 1 : 0;
744 WebPIDecoder* idec;
745 WEBP_CSP_MODE colorspace;
746
747 if (is_external_memory == 0) { // Overwrite parameters to sane values.
748 luma_size = u_size = v_size = a_size = 0;
749 luma_stride = u_stride = v_stride = a_stride = 0;
750 u = v = a = NULL((void*)0);
751 colorspace = MODE_YUVA;
752 } else { // A luma buffer was passed. Validate the other parameters.
753 if (u == NULL((void*)0) || v == NULL((void*)0)) return NULL((void*)0);
754 if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL((void*)0);
755 if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL((void*)0);
756 if (a != NULL((void*)0)) {
757 if (a_size == 0 || a_stride == 0) return NULL((void*)0);
758 }
759 colorspace = (a == NULL((void*)0)) ? MODE_YUV : MODE_YUVA;
760 }
761
762 idec = WebPINewDecoder(NULL((void*)0));
763 if (idec == NULL((void*)0)) return NULL((void*)0);
764
765 idec->output.colorspace = colorspace;
766 idec->output.is_external_memory = is_external_memory;
767 idec->output.u.YUVA.y = luma;
768 idec->output.u.YUVA.y_stride = luma_stride;
769 idec->output.u.YUVA.y_size = luma_size;
770 idec->output.u.YUVA.u = u;
771 idec->output.u.YUVA.u_stride = u_stride;
772 idec->output.u.YUVA.u_size = u_size;
773 idec->output.u.YUVA.v = v;
774 idec->output.u.YUVA.v_stride = v_stride;
775 idec->output.u.YUVA.v_size = v_size;
776 idec->output.u.YUVA.a = a;
777 idec->output.u.YUVA.a_stride = a_stride;
778 idec->output.u.YUVA.a_size = a_size;
779 return idec;
780}
781
782WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
783 uint8_t* u, size_t u_size, int u_stride,
784 uint8_t* v, size_t v_size, int v_stride) {
785 return WebPINewYUVA(luma, luma_size, luma_stride,
786 u, u_size, u_stride,
787 v, v_size, v_stride,
788 NULL((void*)0), 0, 0);
789}
790
791//------------------------------------------------------------------------------
792
793static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
794 assert(idec)((void) sizeof ((idec) ? 1 : 0), __extension__ ({ if (idec) ;
else __assert_fail ("idec", "/root/firefox-clang/media/libwebp/src/dec/idec_dec.c"
, 794, __extension__ __PRETTY_FUNCTION__); }))
;
795 if (idec->state == STATE_ERROR) {
796 return VP8_STATUS_BITSTREAM_ERROR;
797 }
798 if (idec->state == STATE_DONE) {
799 return VP8_STATUS_OK;
800 }
801 return VP8_STATUS_SUSPENDED;
802}
803
804VP8StatusCode WebPIAppend(WebPIDecoder* idec,
805 const uint8_t* data, size_t data_size) {
806 VP8StatusCode status;
807 if (idec == NULL((void*)0) || data == NULL((void*)0)) {
808 return VP8_STATUS_INVALID_PARAM;
809 }
810 status = IDecCheckStatus(idec);
811 if (status != VP8_STATUS_SUSPENDED) {
812 return status;
813 }
814 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
815 if (!CheckMemBufferMode(&idec->mem, MEM_MODE_APPEND)) {
816 return VP8_STATUS_INVALID_PARAM;
817 }
818 // Append data to memory buffer
819 if (!AppendToMemBuffer(idec, data, data_size)) {
820 return VP8_STATUS_OUT_OF_MEMORY;
821 }
822 return IDecode(idec);
823}
824
825VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
826 const uint8_t* data, size_t data_size) {
827 VP8StatusCode status;
828 if (idec == NULL((void*)0) || data == NULL((void*)0)) {
1
Assuming 'idec' is not equal to NULL
2
Assuming 'data' is not equal to NULL
3
Taking false branch
829 return VP8_STATUS_INVALID_PARAM;
830 }
831 status = IDecCheckStatus(idec);
832 if (status
3.1
'status' is equal to VP8_STATUS_SUSPENDED
!= VP8_STATUS_SUSPENDED) {
4
Taking false branch
833 return status;
834 }
835 // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
836 if (!CheckMemBufferMode(&idec->mem, MEM_MODE_MAP)) {
5
Taking false branch
837 return VP8_STATUS_INVALID_PARAM;
838 }
839 // Make the memory buffer point to the new buffer
840 if (!RemapMemBuffer(idec, data, data_size)) {
6
Calling 'RemapMemBuffer'
841 return VP8_STATUS_INVALID_PARAM;
842 }
843 return IDecode(idec);
844}
845
846//------------------------------------------------------------------------------
847
848static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
849 if (idec == NULL((void*)0) || idec->dec == NULL((void*)0)) {
850 return NULL((void*)0);
851 }
852 if (idec->state <= STATE_VP8_PARTS0) {
853 return NULL((void*)0);
854 }
855 if (idec->final_output != NULL((void*)0)) {
856 return NULL((void*)0); // not yet slow-copied
857 }
858 return idec->params.output;
859}
860
861const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
862 int* left, int* top,
863 int* width, int* height) {
864 const WebPDecBuffer* const src = GetOutputBuffer(idec);
865 if (left != NULL((void*)0)) *left = 0;
866 if (top != NULL((void*)0)) *top = 0;
867 if (src != NULL((void*)0)) {
868 if (width != NULL((void*)0)) *width = src->width;
869 if (height != NULL((void*)0)) *height = idec->params.last_y;
870 } else {
871 if (width != NULL((void*)0)) *width = 0;
872 if (height != NULL((void*)0)) *height = 0;
873 }
874 return src;
875}
876
877WEBP_NODISCARD uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
878 int* width, int* height, int* stride) {
879 const WebPDecBuffer* const src = GetOutputBuffer(idec);
880 if (src == NULL((void*)0)) return NULL((void*)0);
881 if (src->colorspace >= MODE_YUV) {
882 return NULL((void*)0);
883 }
884
885 if (last_y != NULL((void*)0)) *last_y = idec->params.last_y;
886 if (width != NULL((void*)0)) *width = src->width;
887 if (height != NULL((void*)0)) *height = src->height;
888 if (stride != NULL((void*)0)) *stride = src->u.RGBA.stride;
889
890 return src->u.RGBA.rgba;
891}
892
893WEBP_NODISCARD uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
894 uint8_t** u, uint8_t** v, uint8_t** a,
895 int* width, int* height, int* stride,
896 int* uv_stride, int* a_stride) {
897 const WebPDecBuffer* const src = GetOutputBuffer(idec);
898 if (src == NULL((void*)0)) return NULL((void*)0);
899 if (src->colorspace < MODE_YUV) {
900 return NULL((void*)0);
901 }
902
903 if (last_y != NULL((void*)0)) *last_y = idec->params.last_y;
904 if (u != NULL((void*)0)) *u = src->u.YUVA.u;
905 if (v != NULL((void*)0)) *v = src->u.YUVA.v;
906 if (a != NULL((void*)0)) *a = src->u.YUVA.a;
907 if (width != NULL((void*)0)) *width = src->width;
908 if (height != NULL((void*)0)) *height = src->height;
909 if (stride != NULL((void*)0)) *stride = src->u.YUVA.y_stride;
910 if (uv_stride != NULL((void*)0)) *uv_stride = src->u.YUVA.u_stride;
911 if (a_stride != NULL((void*)0)) *a_stride = src->u.YUVA.a_stride;
912
913 return src->u.YUVA.y;
914}
915
916int WebPISetIOHooks(WebPIDecoder* const idec,
917 VP8IoPutHook put,
918 VP8IoSetupHook setup,
919 VP8IoTeardownHook teardown,
920 void* user_data) {
921 if (idec == NULL((void*)0) || idec->state > STATE_WEBP_HEADER) {
922 return 0;
923 }
924
925 idec->io.put = put;
926 idec->io.setup = setup;
927 idec->io.teardown = teardown;
928 idec->io.opaque = user_data;
929
930 return 1;
931}