Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/libwebp/src/mux/muxinternal.c
21518 views
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
// Internal objects and utils for mux.
11
//
12
// Authors: Urvang ([email protected])
13
// Vikas ([email protected])
14
15
#include <assert.h>
16
#include <stddef.h>
17
#include <string.h>
18
19
#include "src/mux/muxi.h"
20
#include "src/webp/types.h"
21
#include "src/utils/utils.h"
22
#include "src/webp/format_constants.h"
23
#include "src/webp/mux.h"
24
#include "src/webp/mux_types.h"
25
26
#define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1))
27
28
const ChunkInfo kChunks[] = {
29
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
30
{ MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
31
{ MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE },
32
{ MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE },
33
{ MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
34
{ MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
35
{ MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
36
{ MKFOURCC('E', 'X', 'I', 'F'), WEBP_CHUNK_EXIF, UNDEFINED_CHUNK_SIZE },
37
{ MKFOURCC('X', 'M', 'P', ' '), WEBP_CHUNK_XMP, UNDEFINED_CHUNK_SIZE },
38
{ NIL_TAG, WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
39
40
{ NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE }
41
};
42
43
//------------------------------------------------------------------------------
44
45
int WebPGetMuxVersion(void) {
46
return (MUX_MAJ_VERSION << 16) | (MUX_MIN_VERSION << 8) | MUX_REV_VERSION;
47
}
48
49
//------------------------------------------------------------------------------
50
// Life of a chunk object.
51
52
void ChunkInit(WebPChunk* const chunk) {
53
assert(chunk);
54
memset(chunk, 0, sizeof(*chunk));
55
chunk->tag = NIL_TAG;
56
}
57
58
WebPChunk* ChunkRelease(WebPChunk* const chunk) {
59
WebPChunk* next;
60
if (chunk == NULL) return NULL;
61
if (chunk->owner) {
62
WebPDataClear(&chunk->data);
63
}
64
next = chunk->next;
65
ChunkInit(chunk);
66
return next;
67
}
68
69
//------------------------------------------------------------------------------
70
// Chunk misc methods.
71
72
CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) {
73
int i;
74
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
75
if (tag == kChunks[i].tag) return (CHUNK_INDEX)i;
76
}
77
return IDX_UNKNOWN;
78
}
79
80
WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
81
int i;
82
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
83
if (tag == kChunks[i].tag) return kChunks[i].id;
84
}
85
return WEBP_CHUNK_UNKNOWN;
86
}
87
88
uint32_t ChunkGetTagFromFourCC(const char fourcc[4]) {
89
return MKFOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
90
}
91
92
CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]) {
93
const uint32_t tag = ChunkGetTagFromFourCC(fourcc);
94
return ChunkGetIndexFromTag(tag);
95
}
96
97
//------------------------------------------------------------------------------
98
// Chunk search methods.
99
100
// Returns next chunk in the chunk list with the given tag.
101
static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) {
102
while (chunk != NULL && chunk->tag != tag) {
103
chunk = chunk->next;
104
}
105
return chunk;
106
}
107
108
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
109
uint32_t iter = nth;
110
first = ChunkSearchNextInList(first, tag);
111
if (first == NULL) return NULL;
112
113
while (--iter != 0) {
114
WebPChunk* next_chunk = ChunkSearchNextInList(first->next, tag);
115
if (next_chunk == NULL) break;
116
first = next_chunk;
117
}
118
return ((nth > 0) && (iter > 0)) ? NULL : first;
119
}
120
121
//------------------------------------------------------------------------------
122
// Chunk writer methods.
123
124
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
125
int copy_data, uint32_t tag) {
126
// For internally allocated chunks, always copy data & make it owner of data.
127
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_ANIM].tag) {
128
copy_data = 1;
129
}
130
131
ChunkRelease(chunk);
132
133
if (data != NULL) {
134
if (copy_data) { // Copy data.
135
if (!WebPDataCopy(data, &chunk->data)) return WEBP_MUX_MEMORY_ERROR;
136
chunk->owner = 1; // Chunk is owner of data.
137
} else { // Don't copy data.
138
chunk->data = *data;
139
}
140
}
141
chunk->tag = tag;
142
return WEBP_MUX_OK;
143
}
144
145
WebPMuxError ChunkSetHead(WebPChunk* const chunk,
146
WebPChunk** const chunk_list) {
147
WebPChunk* new_chunk;
148
149
assert(chunk_list != NULL);
150
if (*chunk_list != NULL) {
151
return WEBP_MUX_NOT_FOUND;
152
}
153
154
new_chunk = (WebPChunk*)WebPSafeMalloc(1ULL, sizeof(*new_chunk));
155
if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
156
*new_chunk = *chunk;
157
chunk->owner = 0;
158
new_chunk->next = NULL;
159
*chunk_list = new_chunk;
160
return WEBP_MUX_OK;
161
}
162
163
WebPMuxError ChunkAppend(WebPChunk* const chunk,
164
WebPChunk*** const chunk_list) {
165
WebPMuxError err;
166
assert(chunk_list != NULL && *chunk_list != NULL);
167
168
if (**chunk_list == NULL) {
169
err = ChunkSetHead(chunk, *chunk_list);
170
} else {
171
WebPChunk* last_chunk = **chunk_list;
172
while (last_chunk->next != NULL) last_chunk = last_chunk->next;
173
err = ChunkSetHead(chunk, &last_chunk->next);
174
if (err == WEBP_MUX_OK) *chunk_list = &last_chunk->next;
175
}
176
return err;
177
}
178
179
//------------------------------------------------------------------------------
180
// Chunk deletion method(s).
181
182
WebPChunk* ChunkDelete(WebPChunk* const chunk) {
183
WebPChunk* const next = ChunkRelease(chunk);
184
WebPSafeFree(chunk);
185
return next;
186
}
187
188
void ChunkListDelete(WebPChunk** const chunk_list) {
189
while (*chunk_list != NULL) {
190
*chunk_list = ChunkDelete(*chunk_list);
191
}
192
}
193
194
//------------------------------------------------------------------------------
195
// Chunk serialization methods.
196
197
static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
198
const size_t chunk_size = chunk->data.size;
199
assert(chunk);
200
assert(chunk->tag != NIL_TAG);
201
PutLE32(dst + 0, chunk->tag);
202
PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
203
assert(chunk_size == (uint32_t)chunk_size);
204
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data.bytes, chunk_size);
205
if (chunk_size & 1)
206
dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding.
207
return dst + ChunkDiskSize(chunk);
208
}
209
210
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
211
while (chunk_list != NULL) {
212
dst = ChunkEmit(chunk_list, dst);
213
chunk_list = chunk_list->next;
214
}
215
return dst;
216
}
217
218
size_t ChunkListDiskSize(const WebPChunk* chunk_list) {
219
size_t size = 0;
220
while (chunk_list != NULL) {
221
size += ChunkDiskSize(chunk_list);
222
chunk_list = chunk_list->next;
223
}
224
return size;
225
}
226
227
//------------------------------------------------------------------------------
228
// Life of a MuxImage object.
229
230
void MuxImageInit(WebPMuxImage* const wpi) {
231
assert(wpi);
232
memset(wpi, 0, sizeof(*wpi));
233
}
234
235
WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
236
WebPMuxImage* next;
237
if (wpi == NULL) return NULL;
238
// There should be at most one chunk of 'header', 'alpha', 'img' but we call
239
// ChunkListDelete to be safe
240
ChunkListDelete(&wpi->header);
241
ChunkListDelete(&wpi->alpha);
242
ChunkListDelete(&wpi->img);
243
ChunkListDelete(&wpi->unknown);
244
245
next = wpi->next;
246
MuxImageInit(wpi);
247
return next;
248
}
249
250
//------------------------------------------------------------------------------
251
// MuxImage search methods.
252
253
// Get a reference to appropriate chunk list within an image given chunk tag.
254
static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi,
255
WebPChunkId id) {
256
assert(wpi != NULL);
257
switch (id) {
258
case WEBP_CHUNK_ANMF: return (WebPChunk**)&wpi->header;
259
case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha;
260
case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img;
261
default: return NULL;
262
}
263
}
264
265
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) {
266
int count = 0;
267
const WebPMuxImage* current;
268
for (current = wpi_list; current != NULL; current = current->next) {
269
if (id == WEBP_CHUNK_NIL) {
270
++count; // Special case: count all images.
271
} else {
272
const WebPChunk* const wpi_chunk = *GetChunkListFromId(current, id);
273
if (wpi_chunk != NULL) {
274
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag);
275
if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'.
276
}
277
}
278
}
279
return count;
280
}
281
282
// Outputs a pointer to 'prev_wpi->next',
283
// where 'prev_wpi' is the pointer to the image at position (nth - 1).
284
// Returns true if nth image was found.
285
static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth,
286
WebPMuxImage*** const location) {
287
uint32_t count = 0;
288
assert(wpi_list);
289
*location = wpi_list;
290
291
if (nth == 0) {
292
nth = MuxImageCount(*wpi_list, WEBP_CHUNK_NIL);
293
if (nth == 0) return 0; // Not found.
294
}
295
296
while (*wpi_list != NULL) {
297
WebPMuxImage* const cur_wpi = *wpi_list;
298
++count;
299
if (count == nth) return 1; // Found.
300
wpi_list = &cur_wpi->next;
301
*location = wpi_list;
302
}
303
return 0; // Not found.
304
}
305
306
//------------------------------------------------------------------------------
307
// MuxImage writer methods.
308
309
WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) {
310
WebPMuxImage* new_wpi;
311
312
while (*wpi_list != NULL) {
313
WebPMuxImage* const cur_wpi = *wpi_list;
314
if (cur_wpi->next == NULL) break;
315
wpi_list = &cur_wpi->next;
316
}
317
318
new_wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*new_wpi));
319
if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR;
320
*new_wpi = *wpi;
321
new_wpi->next = NULL;
322
323
if (*wpi_list != NULL) {
324
(*wpi_list)->next = new_wpi;
325
} else {
326
*wpi_list = new_wpi;
327
}
328
return WEBP_MUX_OK;
329
}
330
331
//------------------------------------------------------------------------------
332
// MuxImage deletion methods.
333
334
WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) {
335
// Delete the components of wpi. If wpi is NULL this is a noop.
336
WebPMuxImage* const next = MuxImageRelease(wpi);
337
WebPSafeFree(wpi);
338
return next;
339
}
340
341
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth) {
342
assert(wpi_list);
343
if (!SearchImageToGetOrDelete(wpi_list, nth, &wpi_list)) {
344
return WEBP_MUX_NOT_FOUND;
345
}
346
*wpi_list = MuxImageDelete(*wpi_list);
347
return WEBP_MUX_OK;
348
}
349
350
//------------------------------------------------------------------------------
351
// MuxImage reader methods.
352
353
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
354
WebPMuxImage** wpi) {
355
assert(wpi_list);
356
assert(wpi);
357
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth,
358
(WebPMuxImage***)&wpi_list)) {
359
return WEBP_MUX_NOT_FOUND;
360
}
361
*wpi = (WebPMuxImage*)*wpi_list;
362
return WEBP_MUX_OK;
363
}
364
365
//------------------------------------------------------------------------------
366
// MuxImage serialization methods.
367
368
// Size of an image.
369
size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
370
size_t size = 0;
371
if (wpi->header != NULL) size += ChunkDiskSize(wpi->header);
372
if (wpi->alpha != NULL) size += ChunkDiskSize(wpi->alpha);
373
if (wpi->img != NULL) size += ChunkDiskSize(wpi->img);
374
if (wpi->unknown != NULL) size += ChunkListDiskSize(wpi->unknown);
375
return size;
376
}
377
378
// Special case as ANMF chunk encapsulates other image chunks.
379
static uint8_t* ChunkEmitSpecial(const WebPChunk* const header,
380
size_t total_size, uint8_t* dst) {
381
const size_t header_size = header->data.size;
382
const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE;
383
assert(header->tag == kChunks[IDX_ANMF].tag);
384
PutLE32(dst + 0, header->tag);
385
PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next);
386
assert(header_size == (uint32_t)header_size);
387
memcpy(dst + CHUNK_HEADER_SIZE, header->data.bytes, header_size);
388
if (header_size & 1) {
389
dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding.
390
}
391
return dst + ChunkDiskSize(header);
392
}
393
394
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
395
// Ordering of chunks to be emitted is strictly as follows:
396
// 1. ANMF chunk (if present).
397
// 2. ALPH chunk (if present).
398
// 3. VP8/VP8L chunk.
399
assert(wpi);
400
if (wpi->header != NULL) {
401
dst = ChunkEmitSpecial(wpi->header, MuxImageDiskSize(wpi), dst);
402
}
403
if (wpi->alpha != NULL) dst = ChunkEmit(wpi->alpha, dst);
404
if (wpi->img != NULL) dst = ChunkEmit(wpi->img, dst);
405
if (wpi->unknown != NULL) dst = ChunkListEmit(wpi->unknown, dst);
406
return dst;
407
}
408
409
//------------------------------------------------------------------------------
410
// Helper methods for mux.
411
412
int MuxHasAlpha(const WebPMuxImage* images) {
413
while (images != NULL) {
414
if (images->has_alpha) return 1;
415
images = images->next;
416
}
417
return 0;
418
}
419
420
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) {
421
PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F'));
422
PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
423
assert(size == (uint32_t)size);
424
PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P'));
425
return data + RIFF_HEADER_SIZE;
426
}
427
428
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
429
assert(mux != NULL);
430
switch (id) {
431
case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x;
432
case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp;
433
case WEBP_CHUNK_ANIM: return (WebPChunk**)&mux->anim;
434
case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif;
435
case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp;
436
default: return (WebPChunk**)&mux->unknown;
437
}
438
}
439
440
static int IsNotCompatible(int feature, int num_items) {
441
return (feature != 0) != (num_items > 0);
442
}
443
444
#define NO_FLAG ((WebPFeatureFlags)0)
445
446
// Test basic constraints:
447
// retrieval, maximum number of chunks by index (use -1 to skip)
448
// and feature incompatibility (use NO_FLAG to skip).
449
// On success returns WEBP_MUX_OK and stores the chunk count in *num.
450
static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
451
WebPFeatureFlags feature,
452
uint32_t vp8x_flags,
453
int max, int* num) {
454
const WebPMuxError err =
455
WebPMuxNumChunks(mux, kChunks[idx].id, num);
456
if (err != WEBP_MUX_OK) return err;
457
if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT;
458
if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) {
459
return WEBP_MUX_INVALID_ARGUMENT;
460
}
461
return WEBP_MUX_OK;
462
}
463
464
WebPMuxError MuxValidate(const WebPMux* const mux) {
465
int num_iccp;
466
int num_exif;
467
int num_xmp;
468
int num_anim;
469
int num_frames;
470
int num_vp8x;
471
int num_images;
472
int num_alpha;
473
uint32_t flags;
474
WebPMuxError err;
475
476
// Verify mux is not NULL.
477
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
478
479
// Verify mux has at least one image.
480
if (mux->images == NULL) return WEBP_MUX_INVALID_ARGUMENT;
481
482
err = WebPMuxGetFeatures(mux, &flags);
483
if (err != WEBP_MUX_OK) return err;
484
485
// At most one color profile chunk.
486
err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp);
487
if (err != WEBP_MUX_OK) return err;
488
489
// At most one EXIF metadata.
490
err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif);
491
if (err != WEBP_MUX_OK) return err;
492
493
// At most one XMP metadata.
494
err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
495
if (err != WEBP_MUX_OK) return err;
496
497
// Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
498
// At most one ANIM chunk.
499
err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
500
if (err != WEBP_MUX_OK) return err;
501
err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
502
if (err != WEBP_MUX_OK) return err;
503
504
{
505
const int has_animation = !!(flags & ANIMATION_FLAG);
506
if (has_animation && (num_anim == 0 || num_frames == 0)) {
507
return WEBP_MUX_INVALID_ARGUMENT;
508
}
509
if (!has_animation && (num_anim == 1 || num_frames > 0)) {
510
return WEBP_MUX_INVALID_ARGUMENT;
511
}
512
if (!has_animation) {
513
const WebPMuxImage* images = mux->images;
514
// There can be only one image.
515
if (images == NULL || images->next != NULL) {
516
return WEBP_MUX_INVALID_ARGUMENT;
517
}
518
// Size must match.
519
if (mux->canvas_width > 0) {
520
if (images->width != mux->canvas_width ||
521
images->height != mux->canvas_height) {
522
return WEBP_MUX_INVALID_ARGUMENT;
523
}
524
}
525
}
526
}
527
528
// Verify either VP8X chunk is present OR there is only one elem in
529
// mux->images.
530
err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
531
if (err != WEBP_MUX_OK) return err;
532
err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
533
if (err != WEBP_MUX_OK) return err;
534
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
535
536
// ALPHA_FLAG & alpha chunk(s) are consistent.
537
// Note: ALPHA_FLAG can be set when there is actually no Alpha data present.
538
if (MuxHasAlpha(mux->images)) {
539
if (num_vp8x > 0) {
540
// VP8X chunk is present, so it should contain ALPHA_FLAG.
541
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
542
} else {
543
// VP8X chunk is not present, so ALPH chunks should NOT be present either.
544
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha);
545
if (err != WEBP_MUX_OK) return err;
546
if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT;
547
}
548
}
549
550
return WEBP_MUX_OK;
551
}
552
553
#undef NO_FLAG
554
555
//------------------------------------------------------------------------------
556
557