Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/3rdparty/libwebp/src/mux/muxread.c
16349 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
// Read 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
// Helper method(s).
21
22
// Handy MACRO.
23
#define SWITCH_ID_LIST(INDEX, LIST) \
24
if (idx == (INDEX)) { \
25
const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
26
kChunks[(INDEX)].tag); \
27
if (chunk) { \
28
*data = chunk->data_; \
29
return WEBP_MUX_OK; \
30
} else { \
31
return WEBP_MUX_NOT_FOUND; \
32
} \
33
}
34
35
static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
36
uint32_t nth, WebPData* const data) {
37
assert(mux != NULL);
38
assert(!IsWPI(kChunks[idx].id));
39
WebPDataInit(data);
40
41
SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
42
SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
43
SWITCH_ID_LIST(IDX_ANIM, mux->anim_);
44
SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
45
SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
46
assert(idx != IDX_UNKNOWN);
47
return WEBP_MUX_NOT_FOUND;
48
}
49
#undef SWITCH_ID_LIST
50
51
// Fill the chunk with the given data (includes chunk header bytes), after some
52
// verifications.
53
static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
54
const uint8_t* data, size_t data_size,
55
size_t riff_size, int copy_data) {
56
uint32_t chunk_size;
57
WebPData chunk_data;
58
59
// Sanity checks.
60
if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
61
chunk_size = GetLE32(data + TAG_SIZE);
62
63
{
64
const size_t chunk_disk_size = SizeWithPadding(chunk_size);
65
if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA;
66
if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA;
67
}
68
69
// Data assignment.
70
chunk_data.bytes = data + CHUNK_HEADER_SIZE;
71
chunk_data.size = chunk_size;
72
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
73
}
74
75
int MuxImageFinalize(WebPMuxImage* const wpi) {
76
const WebPChunk* const img = wpi->img_;
77
const WebPData* const image = &img->data_;
78
const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
79
int w, h;
80
int vp8l_has_alpha = 0;
81
const int ok = is_lossless ?
82
VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
83
VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
84
assert(img != NULL);
85
if (ok) {
86
// Ignore ALPH chunk accompanying VP8L.
87
if (is_lossless && (wpi->alpha_ != NULL)) {
88
ChunkDelete(wpi->alpha_);
89
wpi->alpha_ = NULL;
90
}
91
wpi->width_ = w;
92
wpi->height_ = h;
93
wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
94
}
95
return ok;
96
}
97
98
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
99
WebPMuxImage* const wpi) {
100
const uint8_t* bytes = chunk->data_.bytes;
101
size_t size = chunk->data_.size;
102
const uint8_t* const last = bytes + size;
103
WebPChunk subchunk;
104
size_t subchunk_size;
105
ChunkInit(&subchunk);
106
107
assert(chunk->tag_ == kChunks[IDX_ANMF].tag);
108
assert(!wpi->is_partial_);
109
110
// ANMF.
111
{
112
const size_t hdr_size = ANMF_CHUNK_SIZE;
113
const WebPData temp = { bytes, hdr_size };
114
// Each of ANMF chunk contain a header at the beginning. So, its size should
115
// be at least 'hdr_size'.
116
if (size < hdr_size) goto Fail;
117
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
118
}
119
ChunkSetNth(&subchunk, &wpi->header_, 1);
120
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
121
122
// Rest of the chunks.
123
subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
124
bytes += subchunk_size;
125
size -= subchunk_size;
126
127
while (bytes != last) {
128
ChunkInit(&subchunk);
129
if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
130
copy_data) != WEBP_MUX_OK) {
131
goto Fail;
132
}
133
switch (ChunkGetIdFromTag(subchunk.tag_)) {
134
case WEBP_CHUNK_ALPHA:
135
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
136
if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
137
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
138
break;
139
case WEBP_CHUNK_IMAGE:
140
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
141
if (!MuxImageFinalize(wpi)) goto Fail;
142
wpi->is_partial_ = 0; // wpi is completely filled.
143
break;
144
case WEBP_CHUNK_UNKNOWN:
145
if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
146
// before some image chunks.
147
if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
148
break;
149
default:
150
goto Fail;
151
break;
152
}
153
subchunk_size = ChunkDiskSize(&subchunk);
154
bytes += subchunk_size;
155
size -= subchunk_size;
156
}
157
if (wpi->is_partial_) goto Fail;
158
return 1;
159
160
Fail:
161
ChunkRelease(&subchunk);
162
return 0;
163
}
164
165
//------------------------------------------------------------------------------
166
// Create a mux object from WebP-RIFF data.
167
168
WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
169
int version) {
170
size_t riff_size;
171
uint32_t tag;
172
const uint8_t* end;
173
WebPMux* mux = NULL;
174
WebPMuxImage* wpi = NULL;
175
const uint8_t* data;
176
size_t size;
177
WebPChunk chunk;
178
ChunkInit(&chunk);
179
180
// Sanity checks.
181
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
182
return NULL; // version mismatch
183
}
184
if (bitstream == NULL) return NULL;
185
186
data = bitstream->bytes;
187
size = bitstream->size;
188
189
if (data == NULL) return NULL;
190
if (size < RIFF_HEADER_SIZE) return NULL;
191
if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
192
GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
193
return NULL;
194
}
195
196
mux = WebPMuxNew();
197
if (mux == NULL) return NULL;
198
199
if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err;
200
201
tag = GetLE32(data + RIFF_HEADER_SIZE);
202
if (tag != kChunks[IDX_VP8].tag &&
203
tag != kChunks[IDX_VP8L].tag &&
204
tag != kChunks[IDX_VP8X].tag) {
205
goto Err; // First chunk should be VP8, VP8L or VP8X.
206
}
207
208
riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE));
209
if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) {
210
goto Err;
211
} else {
212
if (riff_size < size) { // Redundant data after last chunk.
213
size = riff_size; // To make sure we don't read any data beyond mux_size.
214
}
215
}
216
217
end = data + size;
218
data += RIFF_HEADER_SIZE;
219
size -= RIFF_HEADER_SIZE;
220
221
wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi));
222
if (wpi == NULL) goto Err;
223
MuxImageInit(wpi);
224
225
// Loop over chunks.
226
while (data != end) {
227
size_t data_size;
228
WebPChunkId id;
229
WebPChunk** chunk_list;
230
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
231
copy_data) != WEBP_MUX_OK) {
232
goto Err;
233
}
234
data_size = ChunkDiskSize(&chunk);
235
id = ChunkGetIdFromTag(chunk.tag_);
236
switch (id) {
237
case WEBP_CHUNK_ALPHA:
238
if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
239
if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
240
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
241
break;
242
case WEBP_CHUNK_IMAGE:
243
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
244
if (!MuxImageFinalize(wpi)) goto Err;
245
wpi->is_partial_ = 0; // wpi is completely filled.
246
PushImage:
247
// Add this to mux->images_ list.
248
if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
249
MuxImageInit(wpi); // Reset for reading next image.
250
break;
251
case WEBP_CHUNK_ANMF:
252
if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete.
253
if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
254
ChunkRelease(&chunk);
255
goto PushImage;
256
break;
257
default: // A non-image chunk.
258
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
259
// getting all chunks of an image.
260
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
261
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
262
if (id == WEBP_CHUNK_VP8X) { // grab global specs
263
mux->canvas_width_ = GetLE24(data + 12) + 1;
264
mux->canvas_height_ = GetLE24(data + 15) + 1;
265
}
266
break;
267
}
268
data += data_size;
269
size -= data_size;
270
ChunkInit(&chunk);
271
}
272
273
// Incomplete image.
274
if (wpi->is_partial_) goto Err;
275
276
// Validate mux if complete.
277
if (MuxValidate(mux) != WEBP_MUX_OK) goto Err;
278
279
MuxImageDelete(wpi);
280
return mux; // All OK;
281
282
Err: // Something bad happened.
283
ChunkRelease(&chunk);
284
MuxImageDelete(wpi);
285
WebPMuxDelete(mux);
286
return NULL;
287
}
288
289
//------------------------------------------------------------------------------
290
// Get API(s).
291
292
// Validates that the given mux has a single image.
293
static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) {
294
const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
295
const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF);
296
297
if (num_images == 0) {
298
// No images in mux.
299
return WEBP_MUX_NOT_FOUND;
300
} else if (num_images == 1 && num_frames == 0) {
301
// Valid case (single image).
302
return WEBP_MUX_OK;
303
} else {
304
// Frame case OR an invalid mux.
305
return WEBP_MUX_INVALID_ARGUMENT;
306
}
307
}
308
309
// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L
310
// chunk and canvas size are valid.
311
static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
312
int* width, int* height, uint32_t* flags) {
313
int w, h;
314
uint32_t f = 0;
315
WebPData data;
316
assert(mux != NULL);
317
318
// Check if VP8X chunk is present.
319
if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
320
if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
321
f = GetLE32(data.bytes + 0);
322
w = GetLE24(data.bytes + 4) + 1;
323
h = GetLE24(data.bytes + 7) + 1;
324
} else {
325
const WebPMuxImage* const wpi = mux->images_;
326
// Grab user-forced canvas size as default.
327
w = mux->canvas_width_;
328
h = mux->canvas_height_;
329
if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
330
// single image and not forced canvas size => use dimension of first frame
331
assert(wpi != NULL);
332
w = wpi->width_;
333
h = wpi->height_;
334
}
335
if (wpi != NULL) {
336
if (wpi->has_alpha_) f |= ALPHA_FLAG;
337
}
338
}
339
if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
340
341
if (width != NULL) *width = w;
342
if (height != NULL) *height = h;
343
if (flags != NULL) *flags = f;
344
return WEBP_MUX_OK;
345
}
346
347
WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, int* width, int* height) {
348
if (mux == NULL || width == NULL || height == NULL) {
349
return WEBP_MUX_INVALID_ARGUMENT;
350
}
351
return MuxGetCanvasInfo(mux, width, height, NULL);
352
}
353
354
WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
355
if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
356
return MuxGetCanvasInfo(mux, NULL, NULL, flags);
357
}
358
359
static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
360
int height, uint32_t flags) {
361
const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
362
assert(width >= 1 && height >= 1);
363
assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE);
364
assert(width * (uint64_t)height < MAX_IMAGE_AREA);
365
PutLE32(dst, MKFOURCC('V', 'P', '8', 'X'));
366
PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE);
367
PutLE32(dst + CHUNK_HEADER_SIZE, flags);
368
PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1);
369
PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1);
370
return dst + vp8x_size;
371
}
372
373
// Assemble a single image WebP bitstream from 'wpi'.
374
static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi,
375
WebPData* const bitstream) {
376
uint8_t* dst;
377
378
// Allocate data.
379
const int need_vp8x = (wpi->alpha_ != NULL);
380
const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0;
381
const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0;
382
// Note: No need to output ANMF chunk for a single image.
383
const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size +
384
ChunkDiskSize(wpi->img_);
385
uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size);
386
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
387
388
// Main RIFF header.
389
dst = MuxEmitRiffHeader(data, size);
390
391
if (need_vp8x) {
392
dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X.
393
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
394
}
395
396
// Bitstream.
397
dst = ChunkListEmit(wpi->img_, dst);
398
assert(dst == data + size);
399
400
// Output.
401
bitstream->bytes = data;
402
bitstream->size = size;
403
return WEBP_MUX_OK;
404
}
405
406
WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
407
WebPData* chunk_data) {
408
CHUNK_INDEX idx;
409
if (mux == NULL || fourcc == NULL || chunk_data == NULL) {
410
return WEBP_MUX_INVALID_ARGUMENT;
411
}
412
idx = ChunkGetIndexFromFourCC(fourcc);
413
if (IsWPI(kChunks[idx].id)) { // An image chunk.
414
return WEBP_MUX_INVALID_ARGUMENT;
415
} else if (idx != IDX_UNKNOWN) { // A known chunk type.
416
return MuxGet(mux, idx, 1, chunk_data);
417
} else { // An unknown chunk type.
418
const WebPChunk* const chunk =
419
ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc));
420
if (chunk == NULL) return WEBP_MUX_NOT_FOUND;
421
*chunk_data = chunk->data_;
422
return WEBP_MUX_OK;
423
}
424
}
425
426
static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi,
427
WebPMuxFrameInfo* const info) {
428
// Set some defaults for unrelated fields.
429
info->x_offset = 0;
430
info->y_offset = 0;
431
info->duration = 1;
432
info->dispose_method = WEBP_MUX_DISPOSE_NONE;
433
info->blend_method = WEBP_MUX_BLEND;
434
// Extract data for related fields.
435
info->id = ChunkGetIdFromTag(wpi->img_->tag_);
436
return SynthesizeBitstream(wpi, &info->bitstream);
437
}
438
439
static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi,
440
WebPMuxFrameInfo* const frame) {
441
const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag);
442
const WebPData* frame_data;
443
if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT;
444
assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame().
445
// Get frame chunk.
446
frame_data = &wpi->header_->data_;
447
if (frame_data->size < kChunks[IDX_ANMF].size) return WEBP_MUX_BAD_DATA;
448
// Extract info.
449
frame->x_offset = 2 * GetLE24(frame_data->bytes + 0);
450
frame->y_offset = 2 * GetLE24(frame_data->bytes + 3);
451
{
452
const uint8_t bits = frame_data->bytes[15];
453
frame->duration = GetLE24(frame_data->bytes + 12);
454
frame->dispose_method =
455
(bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
456
frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
457
}
458
frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
459
return SynthesizeBitstream(wpi, &frame->bitstream);
460
}
461
462
WebPMuxError WebPMuxGetFrame(
463
const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) {
464
WebPMuxError err;
465
WebPMuxImage* wpi;
466
467
// Sanity checks.
468
if (mux == NULL || frame == NULL) {
469
return WEBP_MUX_INVALID_ARGUMENT;
470
}
471
472
// Get the nth WebPMuxImage.
473
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi);
474
if (err != WEBP_MUX_OK) return err;
475
476
// Get frame info.
477
if (wpi->header_ == NULL) {
478
return MuxGetImageInternal(wpi, frame);
479
} else {
480
return MuxGetFrameInternal(wpi, frame);
481
}
482
}
483
484
WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux,
485
WebPMuxAnimParams* params) {
486
WebPData anim;
487
WebPMuxError err;
488
489
if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
490
491
err = MuxGet(mux, IDX_ANIM, 1, &anim);
492
if (err != WEBP_MUX_OK) return err;
493
if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA;
494
params->bgcolor = GetLE32(anim.bytes);
495
params->loop_count = GetLE16(anim.bytes + 4);
496
497
return WEBP_MUX_OK;
498
}
499
500
// Get chunk index from chunk id. Returns IDX_NIL if not found.
501
static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) {
502
int i;
503
for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) {
504
if (id == kChunks[i].id) return (CHUNK_INDEX)i;
505
}
506
return IDX_NIL;
507
}
508
509
// Count number of chunks matching 'tag' in the 'chunk_list'.
510
// If tag == NIL_TAG, any tag will be matched.
511
static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) {
512
int count = 0;
513
const WebPChunk* current;
514
for (current = chunk_list; current != NULL; current = current->next_) {
515
if (tag == NIL_TAG || current->tag_ == tag) {
516
count++; // Count chunks whose tags match.
517
}
518
}
519
return count;
520
}
521
522
WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
523
WebPChunkId id, int* num_elements) {
524
if (mux == NULL || num_elements == NULL) {
525
return WEBP_MUX_INVALID_ARGUMENT;
526
}
527
528
if (IsWPI(id)) {
529
*num_elements = MuxImageCount(mux->images_, id);
530
} else {
531
WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
532
const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
533
*num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
534
}
535
536
return WEBP_MUX_OK;
537
}
538
539
//------------------------------------------------------------------------------
540
541