Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/libwebp/src/mux/muxedit.c
21344 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
// Set and delete APIs 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/dec/vp8_dec.h"
20
#include "src/mux/muxi.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
#include "src/webp/types.h"
26
27
//------------------------------------------------------------------------------
28
// Life of a mux object.
29
30
static void MuxInit(WebPMux* const mux) {
31
assert(mux != NULL);
32
memset(mux, 0, sizeof(*mux));
33
mux->canvas_width = 0; // just to be explicit
34
mux->canvas_height = 0;
35
}
36
37
WebPMux* WebPNewInternal(int version) {
38
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
39
return NULL;
40
} else {
41
WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux));
42
if (mux != NULL) MuxInit(mux);
43
return mux;
44
}
45
}
46
47
// Delete all images in 'wpi_list'.
48
static void DeleteAllImages(WebPMuxImage** const wpi_list) {
49
while (*wpi_list != NULL) {
50
*wpi_list = MuxImageDelete(*wpi_list);
51
}
52
}
53
54
static void MuxRelease(WebPMux* const mux) {
55
assert(mux != NULL);
56
DeleteAllImages(&mux->images);
57
ChunkListDelete(&mux->vp8x);
58
ChunkListDelete(&mux->iccp);
59
ChunkListDelete(&mux->anim);
60
ChunkListDelete(&mux->exif);
61
ChunkListDelete(&mux->xmp);
62
ChunkListDelete(&mux->unknown);
63
}
64
65
void WebPMuxDelete(WebPMux* mux) {
66
if (mux != NULL) {
67
MuxRelease(mux);
68
WebPSafeFree(mux);
69
}
70
}
71
72
//------------------------------------------------------------------------------
73
// Helper method(s).
74
75
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
76
#define SWITCH_ID_LIST(INDEX, LIST) \
77
do { \
78
if (idx == (INDEX)) { \
79
err = ChunkAssignData(&chunk, data, copy_data, tag); \
80
if (err == WEBP_MUX_OK) { \
81
err = ChunkSetHead(&chunk, (LIST)); \
82
if (err != WEBP_MUX_OK) ChunkRelease(&chunk); \
83
} \
84
return err; \
85
} \
86
} while (0)
87
88
static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag,
89
const WebPData* const data, int copy_data) {
90
WebPChunk chunk;
91
WebPMuxError err = WEBP_MUX_NOT_FOUND;
92
const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag);
93
assert(mux != NULL);
94
assert(!IsWPI(kChunks[idx].id));
95
96
ChunkInit(&chunk);
97
SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x);
98
SWITCH_ID_LIST(IDX_ICCP, &mux->iccp);
99
SWITCH_ID_LIST(IDX_ANIM, &mux->anim);
100
SWITCH_ID_LIST(IDX_EXIF, &mux->exif);
101
SWITCH_ID_LIST(IDX_XMP, &mux->xmp);
102
SWITCH_ID_LIST(IDX_UNKNOWN, &mux->unknown);
103
return err;
104
}
105
#undef SWITCH_ID_LIST
106
107
// Create data for frame given image data, offsets and duration.
108
static WebPMuxError CreateFrameData(
109
int width, int height, const WebPMuxFrameInfo* const info,
110
WebPData* const frame) {
111
uint8_t* frame_bytes;
112
const size_t frame_size = kChunks[IDX_ANMF].size;
113
114
assert(width > 0 && height > 0 && info->duration >= 0);
115
assert(info->dispose_method == (info->dispose_method & 1));
116
// Note: assertion on upper bounds is done in PutLE24().
117
118
frame_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size);
119
if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
120
121
PutLE24(frame_bytes + 0, info->x_offset / 2);
122
PutLE24(frame_bytes + 3, info->y_offset / 2);
123
124
PutLE24(frame_bytes + 6, width - 1);
125
PutLE24(frame_bytes + 9, height - 1);
126
PutLE24(frame_bytes + 12, info->duration);
127
frame_bytes[15] =
128
(info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) |
129
(info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0);
130
131
frame->bytes = frame_bytes;
132
frame->size = frame_size;
133
return WEBP_MUX_OK;
134
}
135
136
// Outputs image data given a bitstream. The bitstream can either be a
137
// single-image WebP file or raw VP8/VP8L data.
138
// Also outputs 'is_lossless' to be true if the given bitstream is lossless.
139
static WebPMuxError GetImageData(const WebPData* const bitstream,
140
WebPData* const image, WebPData* const alpha,
141
int* const is_lossless) {
142
WebPDataInit(alpha); // Default: no alpha.
143
if (bitstream->size < TAG_SIZE ||
144
memcmp(bitstream->bytes, "RIFF", TAG_SIZE)) {
145
// It is NOT webp file data. Return input data as is.
146
*image = *bitstream;
147
} else {
148
// It is webp file data. Extract image data from it.
149
const WebPMuxImage* wpi;
150
WebPMux* const mux = WebPMuxCreate(bitstream, 0);
151
if (mux == NULL) return WEBP_MUX_BAD_DATA;
152
wpi = mux->images;
153
assert(wpi != NULL && wpi->img != NULL);
154
*image = wpi->img->data;
155
if (wpi->alpha != NULL) {
156
*alpha = wpi->alpha->data;
157
}
158
WebPMuxDelete(mux);
159
}
160
*is_lossless = VP8LCheckSignature(image->bytes, image->size);
161
return WEBP_MUX_OK;
162
}
163
164
static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) {
165
WebPMuxError err = WEBP_MUX_NOT_FOUND;
166
assert(chunk_list);
167
while (*chunk_list) {
168
WebPChunk* const chunk = *chunk_list;
169
if (chunk->tag == tag) {
170
*chunk_list = ChunkDelete(chunk);
171
err = WEBP_MUX_OK;
172
} else {
173
chunk_list = &chunk->next;
174
}
175
}
176
return err;
177
}
178
179
static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, uint32_t tag) {
180
const WebPChunkId id = ChunkGetIdFromTag(tag);
181
assert(mux != NULL);
182
if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
183
return DeleteChunks(MuxGetChunkListFromId(mux, id), tag);
184
}
185
186
//------------------------------------------------------------------------------
187
// Set API(s).
188
189
WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4],
190
const WebPData* chunk_data, int copy_data) {
191
uint32_t tag;
192
WebPMuxError err;
193
if (mux == NULL || fourcc == NULL || chunk_data == NULL ||
194
chunk_data->bytes == NULL || chunk_data->size > MAX_CHUNK_PAYLOAD) {
195
return WEBP_MUX_INVALID_ARGUMENT;
196
}
197
tag = ChunkGetTagFromFourCC(fourcc);
198
199
// Delete existing chunk(s) with the same 'fourcc'.
200
err = MuxDeleteAllNamedData(mux, tag);
201
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
202
203
// Add the given chunk.
204
return MuxSet(mux, tag, chunk_data, copy_data);
205
}
206
207
// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'.
208
static WebPMuxError AddDataToChunkList(
209
const WebPData* const data, int copy_data, uint32_t tag,
210
WebPChunk** chunk_list) {
211
WebPChunk chunk;
212
WebPMuxError err;
213
ChunkInit(&chunk);
214
err = ChunkAssignData(&chunk, data, copy_data, tag);
215
if (err != WEBP_MUX_OK) goto Err;
216
err = ChunkSetHead(&chunk, chunk_list);
217
if (err != WEBP_MUX_OK) goto Err;
218
return WEBP_MUX_OK;
219
Err:
220
ChunkRelease(&chunk);
221
return err;
222
}
223
224
// Extracts image & alpha data from the given bitstream and then sets wpi.alpha
225
// and wpi.img appropriately.
226
static WebPMuxError SetAlphaAndImageChunks(
227
const WebPData* const bitstream, int copy_data, WebPMuxImage* const wpi) {
228
int is_lossless = 0;
229
WebPData image, alpha;
230
WebPMuxError err = GetImageData(bitstream, &image, &alpha, &is_lossless);
231
const int image_tag =
232
is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
233
if (err != WEBP_MUX_OK) return err;
234
if (alpha.bytes != NULL) {
235
err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag,
236
&wpi->alpha);
237
if (err != WEBP_MUX_OK) return err;
238
}
239
err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img);
240
if (err != WEBP_MUX_OK) return err;
241
return MuxImageFinalize(wpi) ? WEBP_MUX_OK : WEBP_MUX_INVALID_ARGUMENT;
242
}
243
244
WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
245
int copy_data) {
246
WebPMuxImage wpi;
247
WebPMuxError err;
248
249
if (mux == NULL || bitstream == NULL || bitstream->bytes == NULL ||
250
bitstream->size > MAX_CHUNK_PAYLOAD) {
251
return WEBP_MUX_INVALID_ARGUMENT;
252
}
253
254
if (mux->images != NULL) {
255
// Only one 'simple image' can be added in mux. So, remove present images.
256
DeleteAllImages(&mux->images);
257
}
258
259
MuxImageInit(&wpi);
260
err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi);
261
if (err != WEBP_MUX_OK) goto Err;
262
263
// Add this WebPMuxImage to mux.
264
err = MuxImagePush(&wpi, &mux->images);
265
if (err != WEBP_MUX_OK) goto Err;
266
267
// All is well.
268
return WEBP_MUX_OK;
269
270
Err: // Something bad happened.
271
MuxImageRelease(&wpi);
272
return err;
273
}
274
275
WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info,
276
int copy_data) {
277
WebPMuxImage wpi;
278
WebPMuxError err;
279
280
if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT;
281
282
if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT;
283
284
if (info->bitstream.bytes == NULL ||
285
info->bitstream.size > MAX_CHUNK_PAYLOAD) {
286
return WEBP_MUX_INVALID_ARGUMENT;
287
}
288
289
if (mux->images != NULL) {
290
const WebPMuxImage* const image = mux->images;
291
const uint32_t image_id = (image->header != NULL) ?
292
ChunkGetIdFromTag(image->header->tag) : WEBP_CHUNK_IMAGE;
293
if (image_id != info->id) {
294
return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types.
295
}
296
}
297
298
MuxImageInit(&wpi);
299
err = SetAlphaAndImageChunks(&info->bitstream, copy_data, &wpi);
300
if (err != WEBP_MUX_OK) goto Err;
301
assert(wpi.img != NULL); // As SetAlphaAndImageChunks() was successful.
302
303
{
304
WebPData frame;
305
const uint32_t tag = kChunks[IDX_ANMF].tag;
306
WebPMuxFrameInfo tmp = *info;
307
tmp.x_offset &= ~1; // Snap offsets to even.
308
tmp.y_offset &= ~1;
309
if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET ||
310
tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET ||
311
(tmp.duration < 0 || tmp.duration >= MAX_DURATION) ||
312
tmp.dispose_method != (tmp.dispose_method & 1)) {
313
err = WEBP_MUX_INVALID_ARGUMENT;
314
goto Err;
315
}
316
err = CreateFrameData(wpi.width, wpi.height, &tmp, &frame);
317
if (err != WEBP_MUX_OK) goto Err;
318
// Add frame chunk (with copy_data = 1).
319
err = AddDataToChunkList(&frame, 1, tag, &wpi.header);
320
WebPDataClear(&frame); // frame owned by wpi.header now.
321
if (err != WEBP_MUX_OK) goto Err;
322
}
323
324
// Add this WebPMuxImage to mux.
325
err = MuxImagePush(&wpi, &mux->images);
326
if (err != WEBP_MUX_OK) goto Err;
327
328
// All is well.
329
return WEBP_MUX_OK;
330
331
Err: // Something bad happened.
332
MuxImageRelease(&wpi);
333
return err;
334
}
335
336
WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
337
const WebPMuxAnimParams* params) {
338
WebPMuxError err;
339
uint8_t data[ANIM_CHUNK_SIZE];
340
const WebPData anim = { data, ANIM_CHUNK_SIZE };
341
342
if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
343
if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) {
344
return WEBP_MUX_INVALID_ARGUMENT;
345
}
346
347
// Delete any existing ANIM chunk(s).
348
err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
349
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
350
351
// Set the animation parameters.
352
PutLE32(data, params->bgcolor);
353
PutLE16(data + 4, params->loop_count);
354
return MuxSet(mux, kChunks[IDX_ANIM].tag, &anim, 1);
355
}
356
357
WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
358
int width, int height) {
359
WebPMuxError err;
360
if (mux == NULL) {
361
return WEBP_MUX_INVALID_ARGUMENT;
362
}
363
if (width < 0 || height < 0 ||
364
width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
365
return WEBP_MUX_INVALID_ARGUMENT;
366
}
367
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
368
return WEBP_MUX_INVALID_ARGUMENT;
369
}
370
if ((width * height) == 0 && (width | height) != 0) {
371
// one of width / height is zero, but not both -> invalid!
372
return WEBP_MUX_INVALID_ARGUMENT;
373
}
374
// If we already assembled a VP8X chunk, invalidate it.
375
err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
376
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
377
378
mux->canvas_width = width;
379
mux->canvas_height = height;
380
return WEBP_MUX_OK;
381
}
382
383
//------------------------------------------------------------------------------
384
// Delete API(s).
385
386
WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) {
387
if (mux == NULL || fourcc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
388
return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc));
389
}
390
391
WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) {
392
if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
393
return MuxImageDeleteNth(&mux->images, nth);
394
}
395
396
//------------------------------------------------------------------------------
397
// Assembly of the WebP RIFF file.
398
399
static WebPMuxError GetFrameInfo(
400
const WebPChunk* const frame_chunk,
401
int* const x_offset, int* const y_offset, int* const duration) {
402
const WebPData* const data = &frame_chunk->data;
403
const size_t expected_data_size = ANMF_CHUNK_SIZE;
404
assert(frame_chunk->tag == kChunks[IDX_ANMF].tag);
405
assert(frame_chunk != NULL);
406
if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
407
408
*x_offset = 2 * GetLE24(data->bytes + 0);
409
*y_offset = 2 * GetLE24(data->bytes + 3);
410
*duration = GetLE24(data->bytes + 12);
411
return WEBP_MUX_OK;
412
}
413
414
static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
415
int* const x_offset, int* const y_offset,
416
int* const duration,
417
int* const width, int* const height) {
418
const WebPChunk* const frame_chunk = wpi->header;
419
WebPMuxError err;
420
assert(wpi != NULL);
421
assert(frame_chunk != NULL);
422
423
// Get offsets and duration from ANMF chunk.
424
err = GetFrameInfo(frame_chunk, x_offset, y_offset, duration);
425
if (err != WEBP_MUX_OK) return err;
426
427
// Get width and height from VP8/VP8L chunk.
428
if (width != NULL) *width = wpi->width;
429
if (height != NULL) *height = wpi->height;
430
return WEBP_MUX_OK;
431
}
432
433
// Returns the tightest dimension for the canvas considering the image list.
434
static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
435
int* const width, int* const height) {
436
WebPMuxImage* wpi = NULL;
437
assert(mux != NULL);
438
assert(width != NULL && height != NULL);
439
440
wpi = mux->images;
441
assert(wpi != NULL);
442
assert(wpi->img != NULL);
443
444
if (wpi->next != NULL) {
445
int max_x = 0, max_y = 0;
446
// if we have a chain of wpi's, header is necessarily set
447
assert(wpi->header != NULL);
448
// Aggregate the bounding box for animation frames.
449
for (; wpi != NULL; wpi = wpi->next) {
450
int x_offset = 0, y_offset = 0, duration = 0, w = 0, h = 0;
451
const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset,
452
&duration, &w, &h);
453
const int max_x_pos = x_offset + w;
454
const int max_y_pos = y_offset + h;
455
if (err != WEBP_MUX_OK) return err;
456
assert(x_offset < MAX_POSITION_OFFSET);
457
assert(y_offset < MAX_POSITION_OFFSET);
458
459
if (max_x_pos > max_x) max_x = max_x_pos;
460
if (max_y_pos > max_y) max_y = max_y_pos;
461
}
462
*width = max_x;
463
*height = max_y;
464
} else {
465
// For a single image, canvas dimensions are same as image dimensions.
466
*width = wpi->width;
467
*height = wpi->height;
468
}
469
return WEBP_MUX_OK;
470
}
471
472
// VP8X format:
473
// Total Size : 10,
474
// Flags : 4 bytes,
475
// Width : 3 bytes,
476
// Height : 3 bytes.
477
static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
478
WebPMuxError err = WEBP_MUX_OK;
479
uint32_t flags = 0;
480
int width = 0;
481
int height = 0;
482
uint8_t data[VP8X_CHUNK_SIZE];
483
const WebPData vp8x = { data, VP8X_CHUNK_SIZE };
484
const WebPMuxImage* images = NULL;
485
486
assert(mux != NULL);
487
images = mux->images; // First image.
488
if (images == NULL || images->img == NULL ||
489
images->img->data.bytes == NULL) {
490
return WEBP_MUX_INVALID_ARGUMENT;
491
}
492
493
// If VP8X chunk(s) is(are) already present, remove them (and later add new
494
// VP8X chunk with updated flags).
495
err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
496
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
497
498
// Set flags.
499
if (mux->iccp != NULL && mux->iccp->data.bytes != NULL) {
500
flags |= ICCP_FLAG;
501
}
502
if (mux->exif != NULL && mux->exif->data.bytes != NULL) {
503
flags |= EXIF_FLAG;
504
}
505
if (mux->xmp != NULL && mux->xmp->data.bytes != NULL) {
506
flags |= XMP_FLAG;
507
}
508
if (images->header != NULL) {
509
if (images->header->tag == kChunks[IDX_ANMF].tag) {
510
// This is an image with animation.
511
flags |= ANIMATION_FLAG;
512
}
513
}
514
if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) {
515
flags |= ALPHA_FLAG; // Some images have an alpha channel.
516
}
517
518
err = GetAdjustedCanvasSize(mux, &width, &height);
519
if (err != WEBP_MUX_OK) return err;
520
521
if (width <= 0 || height <= 0) {
522
return WEBP_MUX_INVALID_ARGUMENT;
523
}
524
if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
525
return WEBP_MUX_INVALID_ARGUMENT;
526
}
527
528
if (mux->canvas_width != 0 || mux->canvas_height != 0) {
529
if (width > mux->canvas_width || height > mux->canvas_height) {
530
return WEBP_MUX_INVALID_ARGUMENT;
531
}
532
width = mux->canvas_width;
533
height = mux->canvas_height;
534
}
535
536
if (flags == 0 && mux->unknown == NULL) {
537
// For simple file format, VP8X chunk should not be added.
538
return WEBP_MUX_OK;
539
}
540
541
if (MuxHasAlpha(images)) {
542
// This means some frames explicitly/implicitly contain alpha.
543
// Note: This 'flags' update must NOT be done for a lossless image
544
// without a VP8X chunk!
545
flags |= ALPHA_FLAG;
546
}
547
548
PutLE32(data + 0, flags); // VP8X chunk flags.
549
PutLE24(data + 4, width - 1); // canvas width.
550
PutLE24(data + 7, height - 1); // canvas height.
551
552
return MuxSet(mux, kChunks[IDX_VP8X].tag, &vp8x, 1);
553
}
554
555
// Cleans up 'mux' by removing any unnecessary chunks.
556
static WebPMuxError MuxCleanup(WebPMux* const mux) {
557
int num_frames;
558
int num_anim_chunks;
559
560
// If we have an image with a single frame, and its rectangle
561
// covers the whole canvas, convert it to a non-animated image
562
// (to avoid writing ANMF chunk unnecessarily).
563
WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
564
if (err != WEBP_MUX_OK) return err;
565
if (num_frames == 1) {
566
WebPMuxImage* frame = NULL;
567
err = MuxImageGetNth((const WebPMuxImage**)&mux->images, 1, &frame);
568
if (err != WEBP_MUX_OK) return err;
569
// We know that one frame does exist.
570
assert(frame != NULL);
571
if (frame->header != NULL &&
572
((mux->canvas_width == 0 && mux->canvas_height == 0) ||
573
(frame->width == mux->canvas_width &&
574
frame->height == mux->canvas_height))) {
575
assert(frame->header->tag == kChunks[IDX_ANMF].tag);
576
ChunkDelete(frame->header); // Removes ANMF chunk.
577
frame->header = NULL;
578
num_frames = 0;
579
}
580
}
581
// Remove ANIM chunk if this is a non-animated image.
582
err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
583
if (err != WEBP_MUX_OK) return err;
584
if (num_anim_chunks >= 1 && num_frames == 0) {
585
err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
586
if (err != WEBP_MUX_OK) return err;
587
}
588
return WEBP_MUX_OK;
589
}
590
591
// Total size of a list of images.
592
static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) {
593
size_t size = 0;
594
while (wpi_list != NULL) {
595
size += MuxImageDiskSize(wpi_list);
596
wpi_list = wpi_list->next;
597
}
598
return size;
599
}
600
601
// Write out the given list of images into 'dst'.
602
static uint8_t* ImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
603
while (wpi_list != NULL) {
604
dst = MuxImageEmit(wpi_list, dst);
605
wpi_list = wpi_list->next;
606
}
607
return dst;
608
}
609
610
WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
611
size_t size = 0;
612
uint8_t* data = NULL;
613
uint8_t* dst = NULL;
614
WebPMuxError err;
615
616
if (assembled_data == NULL) {
617
return WEBP_MUX_INVALID_ARGUMENT;
618
}
619
// Clean up returned data, in case something goes wrong.
620
memset(assembled_data, 0, sizeof(*assembled_data));
621
622
if (mux == NULL) {
623
return WEBP_MUX_INVALID_ARGUMENT;
624
}
625
626
// Finalize mux.
627
err = MuxCleanup(mux);
628
if (err != WEBP_MUX_OK) return err;
629
err = CreateVP8XChunk(mux);
630
if (err != WEBP_MUX_OK) return err;
631
632
// Allocate data.
633
size = ChunkListDiskSize(mux->vp8x) + ChunkListDiskSize(mux->iccp)
634
+ ChunkListDiskSize(mux->anim) + ImageListDiskSize(mux->images)
635
+ ChunkListDiskSize(mux->exif) + ChunkListDiskSize(mux->xmp)
636
+ ChunkListDiskSize(mux->unknown) + RIFF_HEADER_SIZE;
637
638
data = (uint8_t*)WebPSafeMalloc(1ULL, size);
639
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
640
641
// Emit header & chunks.
642
dst = MuxEmitRiffHeader(data, size);
643
dst = ChunkListEmit(mux->vp8x, dst);
644
dst = ChunkListEmit(mux->iccp, dst);
645
dst = ChunkListEmit(mux->anim, dst);
646
dst = ImageListEmit(mux->images, dst);
647
dst = ChunkListEmit(mux->exif, dst);
648
dst = ChunkListEmit(mux->xmp, dst);
649
dst = ChunkListEmit(mux->unknown, dst);
650
assert(dst == data + size);
651
652
// Validate mux.
653
err = MuxValidate(mux);
654
if (err != WEBP_MUX_OK) {
655
WebPSafeFree(data);
656
data = NULL;
657
size = 0;
658
}
659
660
// Finalize data.
661
assembled_data->bytes = data;
662
assembled_data->size = size;
663
664
return err;
665
}
666
667
//------------------------------------------------------------------------------
668
669