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