Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/libwebp/src/demux/demux.c
21689 views
1
// Copyright 2012 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
// WebP container demux.
11
//
12
13
#ifdef HAVE_CONFIG_H
14
#include "src/webp/config.h"
15
#endif
16
17
#include <assert.h>
18
#include <stdlib.h>
19
#include <string.h>
20
21
#include "src/utils/utils.h"
22
#include "src/webp/decode.h" // WebPGetFeatures
23
#include "src/webp/demux.h"
24
#include "src/webp/format_constants.h"
25
#include "src/webp/mux.h"
26
#include "src/webp/mux_types.h"
27
#include "src/webp/types.h"
28
29
#define DMUX_MAJ_VERSION 1
30
#define DMUX_MIN_VERSION 6
31
#define DMUX_REV_VERSION 0
32
33
typedef struct {
34
size_t start; // start location of the data
35
size_t end; // end location
36
size_t riff_end; // riff chunk end location, can be > end.
37
size_t buf_size; // size of the buffer
38
const uint8_t* buf;
39
} MemBuffer;
40
41
typedef struct {
42
size_t offset;
43
size_t size;
44
} ChunkData;
45
46
typedef struct Frame {
47
int x_offset, y_offset;
48
int width, height;
49
int has_alpha;
50
int duration;
51
WebPMuxAnimDispose dispose_method;
52
WebPMuxAnimBlend blend_method;
53
int frame_num;
54
int complete; // img_components contains a full image.
55
ChunkData img_components[2]; // 0=VP8{,L} 1=ALPH
56
struct Frame* next;
57
} Frame;
58
59
typedef struct Chunk {
60
ChunkData data;
61
struct Chunk* next;
62
} Chunk;
63
64
struct WebPDemuxer {
65
MemBuffer mem;
66
WebPDemuxState state;
67
int is_ext_format;
68
uint32_t feature_flags;
69
int canvas_width, canvas_height;
70
int loop_count;
71
uint32_t bgcolor;
72
int num_frames;
73
Frame* frames;
74
Frame** frames_tail;
75
Chunk* chunks; // non-image chunks
76
Chunk** chunks_tail;
77
};
78
79
typedef enum {
80
PARSE_OK,
81
PARSE_NEED_MORE_DATA,
82
PARSE_ERROR
83
} ParseStatus;
84
85
typedef struct ChunkParser {
86
uint8_t id[4];
87
ParseStatus (*parse)(WebPDemuxer* const dmux);
88
int (*valid)(const WebPDemuxer* const dmux);
89
} ChunkParser;
90
91
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
92
static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
93
static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
94
static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
95
96
static const ChunkParser kMasterChunks[] = {
97
{ { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
98
{ { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
99
{ { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
100
{ { '0', '0', '0', '0' }, NULL, NULL },
101
};
102
103
//------------------------------------------------------------------------------
104
105
int WebPGetDemuxVersion(void) {
106
return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
107
}
108
109
// -----------------------------------------------------------------------------
110
// MemBuffer
111
112
static int RemapMemBuffer(MemBuffer* const mem,
113
const uint8_t* data, size_t size) {
114
if (size < mem->buf_size) return 0; // can't remap to a shorter buffer!
115
116
mem->buf = data;
117
mem->end = mem->buf_size = size;
118
return 1;
119
}
120
121
static int InitMemBuffer(MemBuffer* const mem,
122
const uint8_t* data, size_t size) {
123
memset(mem, 0, sizeof(*mem));
124
return RemapMemBuffer(mem, data, size);
125
}
126
127
// Return the remaining data size available in 'mem'.
128
static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
129
return (mem->end - mem->start);
130
}
131
132
// Return true if 'size' exceeds the end of the RIFF chunk.
133
static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
134
return (size > mem->riff_end - mem->start);
135
}
136
137
static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
138
mem->start += size;
139
}
140
141
static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
142
mem->start -= size;
143
}
144
145
static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
146
return mem->buf + mem->start;
147
}
148
149
// Read from 'mem' and skip the read bytes.
150
static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
151
const uint8_t byte = mem->buf[mem->start];
152
Skip(mem, 1);
153
return byte;
154
}
155
156
static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
157
const uint8_t* const data = mem->buf + mem->start;
158
const int val = GetLE16(data);
159
Skip(mem, 2);
160
return val;
161
}
162
163
static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
164
const uint8_t* const data = mem->buf + mem->start;
165
const int val = GetLE24(data);
166
Skip(mem, 3);
167
return val;
168
}
169
170
static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
171
const uint8_t* const data = mem->buf + mem->start;
172
const uint32_t val = GetLE32(data);
173
Skip(mem, 4);
174
return val;
175
}
176
177
// -----------------------------------------------------------------------------
178
// Secondary chunk parsing
179
180
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
181
*dmux->chunks_tail = chunk;
182
chunk->next = NULL;
183
dmux->chunks_tail = &chunk->next;
184
}
185
186
// Add a frame to the end of the list, ensuring the last frame is complete.
187
// Returns true on success, false otherwise.
188
static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
189
const Frame* const last_frame = *dmux->frames_tail;
190
if (last_frame != NULL && !last_frame->complete) return 0;
191
192
*dmux->frames_tail = frame;
193
frame->next = NULL;
194
dmux->frames_tail = &frame->next;
195
return 1;
196
}
197
198
static void SetFrameInfo(size_t start_offset, size_t size,
199
int frame_num, int complete,
200
const WebPBitstreamFeatures* const features,
201
Frame* const frame) {
202
frame->img_components[0].offset = start_offset;
203
frame->img_components[0].size = size;
204
frame->width = features->width;
205
frame->height = features->height;
206
frame->has_alpha |= features->has_alpha;
207
frame->frame_num = frame_num;
208
frame->complete = complete;
209
}
210
211
// Store image bearing chunks to 'frame'. 'min_size' is an optional size
212
// requirement, it may be zero.
213
static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
214
MemBuffer* const mem, Frame* const frame) {
215
int alpha_chunks = 0;
216
int image_chunks = 0;
217
int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE ||
218
MemDataSize(mem) < min_size);
219
ParseStatus status = PARSE_OK;
220
221
if (done) return PARSE_NEED_MORE_DATA;
222
223
do {
224
const size_t chunk_start_offset = mem->start;
225
const uint32_t fourcc = ReadLE32(mem);
226
const uint32_t payload_size = ReadLE32(mem);
227
uint32_t payload_size_padded;
228
size_t payload_available;
229
size_t chunk_size;
230
231
if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
232
233
payload_size_padded = payload_size + (payload_size & 1);
234
payload_available = (payload_size_padded > MemDataSize(mem))
235
? MemDataSize(mem) : payload_size_padded;
236
chunk_size = CHUNK_HEADER_SIZE + payload_available;
237
if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
238
if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
239
240
switch (fourcc) {
241
case MKFOURCC('A', 'L', 'P', 'H'):
242
if (alpha_chunks == 0) {
243
++alpha_chunks;
244
frame->img_components[1].offset = chunk_start_offset;
245
frame->img_components[1].size = chunk_size;
246
frame->has_alpha = 1;
247
frame->frame_num = frame_num;
248
Skip(mem, payload_available);
249
} else {
250
goto Done;
251
}
252
break;
253
case MKFOURCC('V', 'P', '8', 'L'):
254
if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
255
// fall through
256
case MKFOURCC('V', 'P', '8', ' '):
257
if (image_chunks == 0) {
258
// Extract the bitstream features, tolerating failures when the data
259
// is incomplete.
260
WebPBitstreamFeatures features;
261
const VP8StatusCode vp8_status =
262
WebPGetFeatures(mem->buf + chunk_start_offset, chunk_size,
263
&features);
264
if (status == PARSE_NEED_MORE_DATA &&
265
vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
266
return PARSE_NEED_MORE_DATA;
267
} else if (vp8_status != VP8_STATUS_OK) {
268
// We have enough data, and yet WebPGetFeatures() failed.
269
return PARSE_ERROR;
270
}
271
++image_chunks;
272
SetFrameInfo(chunk_start_offset, chunk_size, frame_num,
273
status == PARSE_OK, &features, frame);
274
Skip(mem, payload_available);
275
} else {
276
goto Done;
277
}
278
break;
279
Done:
280
default:
281
// Restore fourcc/size when moving up one level in parsing.
282
Rewind(mem, CHUNK_HEADER_SIZE);
283
done = 1;
284
break;
285
}
286
287
if (mem->start == mem->riff_end) {
288
done = 1;
289
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
290
status = PARSE_NEED_MORE_DATA;
291
}
292
} while (!done && status == PARSE_OK);
293
294
return status;
295
}
296
297
// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
298
// enough data ('min_size') to parse the payload.
299
// Returns PARSE_OK on success with *frame pointing to the new Frame.
300
// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
301
static ParseStatus NewFrame(const MemBuffer* const mem,
302
uint32_t min_size, uint32_t actual_size,
303
Frame** frame) {
304
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
305
if (actual_size < min_size) return PARSE_ERROR;
306
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
307
308
*frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
309
return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
310
}
311
312
// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
313
// 'frame_chunk_size' is the previously validated, padded chunk size.
314
static ParseStatus ParseAnimationFrame(
315
WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
316
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
317
const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
318
int added_frame = 0;
319
int bits;
320
MemBuffer* const mem = &dmux->mem;
321
Frame* frame;
322
size_t start_offset;
323
ParseStatus status =
324
NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
325
if (status != PARSE_OK) return status;
326
327
frame->x_offset = 2 * ReadLE24s(mem);
328
frame->y_offset = 2 * ReadLE24s(mem);
329
frame->width = 1 + ReadLE24s(mem);
330
frame->height = 1 + ReadLE24s(mem);
331
frame->duration = ReadLE24s(mem);
332
bits = ReadByte(mem);
333
frame->dispose_method =
334
(bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
335
frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
336
if (frame->width * (uint64_t)frame->height >= MAX_IMAGE_AREA) {
337
WebPSafeFree(frame);
338
return PARSE_ERROR;
339
}
340
341
// Store a frame only if the animation flag is set there is some data for
342
// this frame is available.
343
start_offset = mem->start;
344
status = StoreFrame(dmux->num_frames + 1, anmf_payload_size, mem, frame);
345
if (status != PARSE_ERROR && mem->start - start_offset > anmf_payload_size) {
346
status = PARSE_ERROR;
347
}
348
if (status != PARSE_ERROR && is_animation && frame->frame_num > 0) {
349
added_frame = AddFrame(dmux, frame);
350
if (added_frame) {
351
++dmux->num_frames;
352
} else {
353
status = PARSE_ERROR;
354
}
355
}
356
357
if (!added_frame) WebPSafeFree(frame);
358
return status;
359
}
360
361
// General chunk storage, starting with the header at 'start_offset', allowing
362
// the user to request the payload via a fourcc string. 'size' includes the
363
// header and the unpadded payload size.
364
// Returns true on success, false otherwise.
365
static int StoreChunk(WebPDemuxer* const dmux,
366
size_t start_offset, uint32_t size) {
367
Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
368
if (chunk == NULL) return 0;
369
370
chunk->data.offset = start_offset;
371
chunk->data.size = size;
372
AddChunk(dmux, chunk);
373
return 1;
374
}
375
376
// -----------------------------------------------------------------------------
377
// Primary chunk parsing
378
379
static ParseStatus ReadHeader(MemBuffer* const mem) {
380
const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
381
uint32_t riff_size;
382
383
// Basic file level validation.
384
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
385
if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
386
memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
387
return PARSE_ERROR;
388
}
389
390
riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
391
if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
392
if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
393
394
// There's no point in reading past the end of the RIFF chunk
395
mem->riff_end = riff_size + CHUNK_HEADER_SIZE;
396
if (mem->buf_size > mem->riff_end) {
397
mem->buf_size = mem->end = mem->riff_end;
398
}
399
400
Skip(mem, RIFF_HEADER_SIZE);
401
return PARSE_OK;
402
}
403
404
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
405
const size_t min_size = CHUNK_HEADER_SIZE;
406
MemBuffer* const mem = &dmux->mem;
407
Frame* frame;
408
ParseStatus status;
409
int image_added = 0;
410
411
if (dmux->frames != NULL) return PARSE_ERROR;
412
if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
413
if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
414
415
frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
416
if (frame == NULL) return PARSE_ERROR;
417
418
// For the single image case we allow parsing of a partial frame, so no
419
// minimum size is imposed here.
420
status = StoreFrame(1, 0, &dmux->mem, frame);
421
if (status != PARSE_ERROR) {
422
const int has_alpha = !!(dmux->feature_flags & ALPHA_FLAG);
423
// Clear any alpha when the alpha flag is missing.
424
if (!has_alpha && frame->img_components[1].size > 0) {
425
frame->img_components[1].offset = 0;
426
frame->img_components[1].size = 0;
427
frame->has_alpha = 0;
428
}
429
430
// Use the frame width/height as the canvas values for non-vp8x files.
431
// Also, set ALPHA_FLAG if this is a lossless image with alpha.
432
if (!dmux->is_ext_format && frame->width > 0 && frame->height > 0) {
433
dmux->state = WEBP_DEMUX_PARSED_HEADER;
434
dmux->canvas_width = frame->width;
435
dmux->canvas_height = frame->height;
436
dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;
437
}
438
if (!AddFrame(dmux, frame)) {
439
status = PARSE_ERROR; // last frame was left incomplete
440
} else {
441
image_added = 1;
442
dmux->num_frames = 1;
443
}
444
}
445
446
if (!image_added) WebPSafeFree(frame);
447
return status;
448
}
449
450
static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
451
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
452
MemBuffer* const mem = &dmux->mem;
453
int anim_chunks = 0;
454
ParseStatus status = PARSE_OK;
455
456
do {
457
int store_chunk = 1;
458
const size_t chunk_start_offset = mem->start;
459
const uint32_t fourcc = ReadLE32(mem);
460
const uint32_t chunk_size = ReadLE32(mem);
461
uint32_t chunk_size_padded;
462
463
if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
464
465
chunk_size_padded = chunk_size + (chunk_size & 1);
466
if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
467
468
switch (fourcc) {
469
case MKFOURCC('V', 'P', '8', 'X'): {
470
return PARSE_ERROR;
471
}
472
case MKFOURCC('A', 'L', 'P', 'H'):
473
case MKFOURCC('V', 'P', '8', ' '):
474
case MKFOURCC('V', 'P', '8', 'L'): {
475
// check that this isn't an animation (all frames should be in an ANMF).
476
if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
477
478
Rewind(mem, CHUNK_HEADER_SIZE);
479
status = ParseSingleImage(dmux);
480
break;
481
}
482
case MKFOURCC('A', 'N', 'I', 'M'): {
483
if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
484
485
if (MemDataSize(mem) < chunk_size_padded) {
486
status = PARSE_NEED_MORE_DATA;
487
} else if (anim_chunks == 0) {
488
++anim_chunks;
489
dmux->bgcolor = ReadLE32(mem);
490
dmux->loop_count = ReadLE16s(mem);
491
Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
492
} else {
493
store_chunk = 0;
494
goto Skip;
495
}
496
break;
497
}
498
case MKFOURCC('A', 'N', 'M', 'F'): {
499
if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
500
status = ParseAnimationFrame(dmux, chunk_size_padded);
501
break;
502
}
503
case MKFOURCC('I', 'C', 'C', 'P'): {
504
store_chunk = !!(dmux->feature_flags & ICCP_FLAG);
505
goto Skip;
506
}
507
case MKFOURCC('E', 'X', 'I', 'F'): {
508
store_chunk = !!(dmux->feature_flags & EXIF_FLAG);
509
goto Skip;
510
}
511
case MKFOURCC('X', 'M', 'P', ' '): {
512
store_chunk = !!(dmux->feature_flags & XMP_FLAG);
513
goto Skip;
514
}
515
Skip:
516
default: {
517
if (chunk_size_padded <= MemDataSize(mem)) {
518
if (store_chunk) {
519
// Store only the chunk header and unpadded size as only the payload
520
// will be returned to the user.
521
if (!StoreChunk(dmux, chunk_start_offset,
522
CHUNK_HEADER_SIZE + chunk_size)) {
523
return PARSE_ERROR;
524
}
525
}
526
Skip(mem, chunk_size_padded);
527
} else {
528
status = PARSE_NEED_MORE_DATA;
529
}
530
}
531
}
532
533
if (mem->start == mem->riff_end) {
534
break;
535
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
536
status = PARSE_NEED_MORE_DATA;
537
}
538
} while (status == PARSE_OK);
539
540
return status;
541
}
542
543
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
544
MemBuffer* const mem = &dmux->mem;
545
uint32_t vp8x_size;
546
547
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
548
549
dmux->is_ext_format = 1;
550
Skip(mem, TAG_SIZE); // VP8X
551
vp8x_size = ReadLE32(mem);
552
if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
553
if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
554
vp8x_size += vp8x_size & 1;
555
if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
556
if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
557
558
dmux->feature_flags = ReadByte(mem);
559
Skip(mem, 3); // Reserved.
560
dmux->canvas_width = 1 + ReadLE24s(mem);
561
dmux->canvas_height = 1 + ReadLE24s(mem);
562
if (dmux->canvas_width * (uint64_t)dmux->canvas_height >= MAX_IMAGE_AREA) {
563
return PARSE_ERROR; // image final dimension is too large
564
}
565
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
566
dmux->state = WEBP_DEMUX_PARSED_HEADER;
567
568
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
569
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
570
571
return ParseVP8XChunks(dmux);
572
}
573
574
// -----------------------------------------------------------------------------
575
// Format validation
576
577
static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
578
const Frame* const frame = dmux->frames;
579
if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;
580
581
if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;
582
if (dmux->state == WEBP_DEMUX_DONE && frame == NULL) return 0;
583
584
if (frame->width <= 0 || frame->height <= 0) return 0;
585
return 1;
586
}
587
588
// If 'exact' is true, check that the image resolution matches the canvas.
589
// If 'exact' is false, check that the x/y offsets do not exceed the canvas.
590
static int CheckFrameBounds(const Frame* const frame, int exact,
591
int canvas_width, int canvas_height) {
592
if (exact) {
593
if (frame->x_offset != 0 || frame->y_offset != 0) {
594
return 0;
595
}
596
if (frame->width != canvas_width || frame->height != canvas_height) {
597
return 0;
598
}
599
} else {
600
if (frame->x_offset < 0 || frame->y_offset < 0) return 0;
601
if (frame->width + frame->x_offset > canvas_width) return 0;
602
if (frame->height + frame->y_offset > canvas_height) return 0;
603
}
604
return 1;
605
}
606
607
static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
608
const int is_animation = !!(dmux->feature_flags & ANIMATION_FLAG);
609
const Frame* f = dmux->frames;
610
611
if (dmux->state == WEBP_DEMUX_PARSING_HEADER) return 1;
612
613
if (dmux->canvas_width <= 0 || dmux->canvas_height <= 0) return 0;
614
if (dmux->loop_count < 0) return 0;
615
if (dmux->state == WEBP_DEMUX_DONE && dmux->frames == NULL) return 0;
616
if (dmux->feature_flags & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
617
618
while (f != NULL) {
619
const int cur_frame_set = f->frame_num;
620
621
// Check frame properties.
622
for (; f != NULL && f->frame_num == cur_frame_set; f = f->next) {
623
const ChunkData* const image = f->img_components;
624
const ChunkData* const alpha = f->img_components + 1;
625
626
if (!is_animation && f->frame_num > 1) return 0;
627
628
if (f->complete) {
629
if (alpha->size == 0 && image->size == 0) return 0;
630
// Ensure alpha precedes image bitstream.
631
if (alpha->size > 0 && alpha->offset > image->offset) {
632
return 0;
633
}
634
635
if (f->width <= 0 || f->height <= 0) return 0;
636
} else {
637
// There shouldn't be a partial frame in a complete file.
638
if (dmux->state == WEBP_DEMUX_DONE) return 0;
639
640
// Ensure alpha precedes image bitstream.
641
if (alpha->size > 0 && image->size > 0 &&
642
alpha->offset > image->offset) {
643
return 0;
644
}
645
// There shouldn't be any frames after an incomplete one.
646
if (f->next != NULL) return 0;
647
}
648
649
if (f->width > 0 && f->height > 0 &&
650
!CheckFrameBounds(f, !is_animation,
651
dmux->canvas_width, dmux->canvas_height)) {
652
return 0;
653
}
654
}
655
}
656
return 1;
657
}
658
659
// -----------------------------------------------------------------------------
660
// WebPDemuxer object
661
662
static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
663
dmux->state = WEBP_DEMUX_PARSING_HEADER;
664
dmux->loop_count = 1;
665
dmux->bgcolor = 0xFFFFFFFF; // White background by default.
666
dmux->canvas_width = -1;
667
dmux->canvas_height = -1;
668
dmux->frames_tail = &dmux->frames;
669
dmux->chunks_tail = &dmux->chunks;
670
dmux->mem = *mem;
671
}
672
673
static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
674
WebPDemuxer** demuxer) {
675
WebPBitstreamFeatures features;
676
const VP8StatusCode status =
677
WebPGetFeatures(mem->buf, mem->buf_size, &features);
678
*demuxer = NULL;
679
if (status != VP8_STATUS_OK) {
680
return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA
681
: PARSE_ERROR;
682
}
683
684
{
685
WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
686
Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
687
if (dmux == NULL || frame == NULL) goto Error;
688
InitDemux(dmux, mem);
689
SetFrameInfo(0, mem->buf_size, 1 /*frame_num*/, 1 /*complete*/, &features,
690
frame);
691
if (!AddFrame(dmux, frame)) goto Error;
692
dmux->state = WEBP_DEMUX_DONE;
693
dmux->canvas_width = frame->width;
694
dmux->canvas_height = frame->height;
695
dmux->feature_flags |= frame->has_alpha ? ALPHA_FLAG : 0;
696
dmux->num_frames = 1;
697
assert(IsValidSimpleFormat(dmux));
698
*demuxer = dmux;
699
return PARSE_OK;
700
701
Error:
702
WebPSafeFree(dmux);
703
WebPSafeFree(frame);
704
return PARSE_ERROR;
705
}
706
}
707
708
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
709
WebPDemuxState* state, int version) {
710
const ChunkParser* parser;
711
int partial;
712
ParseStatus status = PARSE_ERROR;
713
MemBuffer mem;
714
WebPDemuxer* dmux;
715
716
if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
717
718
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
719
if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
720
721
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
722
status = ReadHeader(&mem);
723
if (status != PARSE_OK) {
724
// If parsing of the webp file header fails attempt to handle a raw
725
// VP8/VP8L frame. Note 'allow_partial' is ignored in this case.
726
if (status == PARSE_ERROR) {
727
status = CreateRawImageDemuxer(&mem, &dmux);
728
if (status == PARSE_OK) {
729
if (state != NULL) *state = WEBP_DEMUX_DONE;
730
return dmux;
731
}
732
}
733
if (state != NULL) {
734
*state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
735
: WEBP_DEMUX_PARSE_ERROR;
736
}
737
return NULL;
738
}
739
740
partial = (mem.buf_size < mem.riff_end);
741
if (!allow_partial && partial) return NULL;
742
743
dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
744
if (dmux == NULL) return NULL;
745
InitDemux(dmux, &mem);
746
747
status = PARSE_ERROR;
748
for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
749
if (!memcmp(parser->id, GetBuffer(&dmux->mem), TAG_SIZE)) {
750
status = parser->parse(dmux);
751
if (status == PARSE_OK) dmux->state = WEBP_DEMUX_DONE;
752
if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
753
if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
754
if (status == PARSE_ERROR) dmux->state = WEBP_DEMUX_PARSE_ERROR;
755
break;
756
}
757
}
758
if (state != NULL) *state = dmux->state;
759
760
if (status == PARSE_ERROR) {
761
WebPDemuxDelete(dmux);
762
return NULL;
763
}
764
return dmux;
765
}
766
767
void WebPDemuxDelete(WebPDemuxer* dmux) {
768
Chunk* c;
769
Frame* f;
770
if (dmux == NULL) return;
771
772
for (f = dmux->frames; f != NULL;) {
773
Frame* const cur_frame = f;
774
f = f->next;
775
WebPSafeFree(cur_frame);
776
}
777
for (c = dmux->chunks; c != NULL;) {
778
Chunk* const cur_chunk = c;
779
c = c->next;
780
WebPSafeFree(cur_chunk);
781
}
782
WebPSafeFree(dmux);
783
}
784
785
// -----------------------------------------------------------------------------
786
787
uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
788
if (dmux == NULL) return 0;
789
790
switch (feature) {
791
case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags;
792
case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width;
793
case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height;
794
case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count;
795
case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor;
796
case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames;
797
}
798
return 0;
799
}
800
801
// -----------------------------------------------------------------------------
802
// Frame iteration
803
804
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
805
const Frame* f;
806
for (f = dmux->frames; f != NULL; f = f->next) {
807
if (frame_num == f->frame_num) break;
808
}
809
return f;
810
}
811
812
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
813
const Frame* const frame,
814
size_t* const data_size) {
815
*data_size = 0;
816
if (frame != NULL) {
817
const ChunkData* const image = frame->img_components;
818
const ChunkData* const alpha = frame->img_components + 1;
819
size_t start_offset = image->offset;
820
*data_size = image->size;
821
822
// if alpha exists it precedes image, update the size allowing for
823
// intervening chunks.
824
if (alpha->size > 0) {
825
const size_t inter_size = (image->offset > 0)
826
? image->offset - (alpha->offset + alpha->size)
827
: 0;
828
start_offset = alpha->offset;
829
*data_size += alpha->size + inter_size;
830
}
831
return mem_buf + start_offset;
832
}
833
return NULL;
834
}
835
836
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
837
static int SynthesizeFrame(const WebPDemuxer* const dmux,
838
const Frame* const frame,
839
WebPIterator* const iter) {
840
const uint8_t* const mem_buf = dmux->mem.buf;
841
size_t payload_size = 0;
842
const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
843
if (payload == NULL) return 0;
844
assert(frame != NULL);
845
846
iter->frame_num = frame->frame_num;
847
iter->num_frames = dmux->num_frames;
848
iter->x_offset = frame->x_offset;
849
iter->y_offset = frame->y_offset;
850
iter->width = frame->width;
851
iter->height = frame->height;
852
iter->has_alpha = frame->has_alpha;
853
iter->duration = frame->duration;
854
iter->dispose_method = frame->dispose_method;
855
iter->blend_method = frame->blend_method;
856
iter->complete = frame->complete;
857
iter->fragment.bytes = payload;
858
iter->fragment.size = payload_size;
859
return 1;
860
}
861
862
static int SetFrame(int frame_num, WebPIterator* const iter) {
863
const Frame* frame;
864
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
865
if (dmux == NULL || frame_num < 0) return 0;
866
if (frame_num > dmux->num_frames) return 0;
867
if (frame_num == 0) frame_num = dmux->num_frames;
868
869
frame = GetFrame(dmux, frame_num);
870
if (frame == NULL) return 0;
871
872
return SynthesizeFrame(dmux, frame, iter);
873
}
874
875
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
876
if (iter == NULL) return 0;
877
878
memset(iter, 0, sizeof(*iter));
879
iter->private_ = (void*)dmux;
880
return SetFrame(frame, iter);
881
}
882
883
int WebPDemuxNextFrame(WebPIterator* iter) {
884
if (iter == NULL) return 0;
885
return SetFrame(iter->frame_num + 1, iter);
886
}
887
888
int WebPDemuxPrevFrame(WebPIterator* iter) {
889
if (iter == NULL) return 0;
890
if (iter->frame_num <= 1) return 0;
891
return SetFrame(iter->frame_num - 1, iter);
892
}
893
894
void WebPDemuxReleaseIterator(WebPIterator* iter) {
895
(void)iter;
896
}
897
898
// -----------------------------------------------------------------------------
899
// Chunk iteration
900
901
static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
902
const uint8_t* const mem_buf = dmux->mem.buf;
903
const Chunk* c;
904
int count = 0;
905
for (c = dmux->chunks; c != NULL; c = c->next) {
906
const uint8_t* const header = mem_buf + c->data.offset;
907
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
908
}
909
return count;
910
}
911
912
static const Chunk* GetChunk(const WebPDemuxer* const dmux,
913
const char fourcc[4], int chunk_num) {
914
const uint8_t* const mem_buf = dmux->mem.buf;
915
const Chunk* c;
916
int count = 0;
917
for (c = dmux->chunks; c != NULL; c = c->next) {
918
const uint8_t* const header = mem_buf + c->data.offset;
919
if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
920
if (count == chunk_num) break;
921
}
922
return c;
923
}
924
925
static int SetChunk(const char fourcc[4], int chunk_num,
926
WebPChunkIterator* const iter) {
927
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
928
int count;
929
930
if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
931
count = ChunkCount(dmux, fourcc);
932
if (count == 0) return 0;
933
if (chunk_num == 0) chunk_num = count;
934
935
if (chunk_num <= count) {
936
const uint8_t* const mem_buf = dmux->mem.buf;
937
const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
938
iter->chunk.bytes = mem_buf + chunk->data.offset + CHUNK_HEADER_SIZE;
939
iter->chunk.size = chunk->data.size - CHUNK_HEADER_SIZE;
940
iter->num_chunks = count;
941
iter->chunk_num = chunk_num;
942
return 1;
943
}
944
return 0;
945
}
946
947
int WebPDemuxGetChunk(const WebPDemuxer* dmux,
948
const char fourcc[4], int chunk_num,
949
WebPChunkIterator* iter) {
950
if (iter == NULL) return 0;
951
952
memset(iter, 0, sizeof(*iter));
953
iter->private_ = (void*)dmux;
954
return SetChunk(fourcc, chunk_num, iter);
955
}
956
957
int WebPDemuxNextChunk(WebPChunkIterator* iter) {
958
if (iter != NULL) {
959
const char* const fourcc =
960
(const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
961
return SetChunk(fourcc, iter->chunk_num + 1, iter);
962
}
963
return 0;
964
}
965
966
int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
967
if (iter != NULL && iter->chunk_num > 1) {
968
const char* const fourcc =
969
(const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
970
return SetChunk(fourcc, iter->chunk_num - 1, iter);
971
}
972
return 0;
973
}
974
975
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
976
(void)iter;
977
}
978
979