go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

muxinternal.c (17968B)


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