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