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