Blame src/mux/muxread.c

Packit 9c6abc
// Copyright 2011 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// Read APIs for mux.
Packit 9c6abc
//
Packit 9c6abc
// Authors: Urvang (urvang@google.com)
Packit 9c6abc
//          Vikas (vikasa@google.com)
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include "src/mux/muxi.h"
Packit 9c6abc
#include "src/utils/utils.h"
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Helper method(s).
Packit 9c6abc
Packit 9c6abc
// Handy MACRO.
Packit 9c6abc
#define SWITCH_ID_LIST(INDEX, LIST)                                           \
Packit 9c6abc
  if (idx == (INDEX)) {                                                       \
Packit 9c6abc
    const WebPChunk* const chunk = ChunkSearchList((LIST), nth,               \
Packit 9c6abc
                                                   kChunks[(INDEX)].tag);     \
Packit 9c6abc
    if (chunk) {                                                              \
Packit 9c6abc
      *data = chunk->data_;                                                   \
Packit 9c6abc
      return WEBP_MUX_OK;                                                     \
Packit 9c6abc
    } else {                                                                  \
Packit 9c6abc
      return WEBP_MUX_NOT_FOUND;                                              \
Packit 9c6abc
    }                                                                         \
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
Packit 9c6abc
                           uint32_t nth, WebPData* const data) {
Packit 9c6abc
  assert(mux != NULL);
Packit 9c6abc
  assert(!IsWPI(kChunks[idx].id));
Packit 9c6abc
  WebPDataInit(data);
Packit 9c6abc
Packit 9c6abc
  SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
Packit 9c6abc
  SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
Packit 9c6abc
  SWITCH_ID_LIST(IDX_ANIM, mux->anim_);
Packit 9c6abc
  SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
Packit 9c6abc
  SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
Packit 9c6abc
  assert(idx != IDX_UNKNOWN);
Packit 9c6abc
  return WEBP_MUX_NOT_FOUND;
Packit 9c6abc
}
Packit 9c6abc
#undef SWITCH_ID_LIST
Packit 9c6abc
Packit 9c6abc
// Fill the chunk with the given data (includes chunk header bytes), after some
Packit 9c6abc
// verifications.
Packit 9c6abc
static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
Packit 9c6abc
                                         const uint8_t* data, size_t data_size,
Packit 9c6abc
                                         size_t riff_size, int copy_data) {
Packit 9c6abc
  uint32_t chunk_size;
Packit 9c6abc
  WebPData chunk_data;
Packit 9c6abc
Packit 9c6abc
  // Sanity checks.
Packit 9c6abc
  if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
Packit 9c6abc
  chunk_size = GetLE32(data + TAG_SIZE);
Packit 9c6abc
Packit 9c6abc
  {
Packit 9c6abc
    const size_t chunk_disk_size = SizeWithPadding(chunk_size);
Packit 9c6abc
    if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA;
Packit 9c6abc
    if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Data assignment.
Packit 9c6abc
  chunk_data.bytes = data + CHUNK_HEADER_SIZE;
Packit 9c6abc
  chunk_data.size = chunk_size;
Packit 9c6abc
  return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
int MuxImageFinalize(WebPMuxImage* const wpi) {
Packit 9c6abc
  const WebPChunk* const img = wpi->img_;
Packit 9c6abc
  const WebPData* const image = &img->data_;
Packit 9c6abc
  const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
Packit 9c6abc
  int w, h;
Packit 9c6abc
  int vp8l_has_alpha = 0;
Packit 9c6abc
  const int ok = is_lossless ?
Packit 9c6abc
      VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
Packit 9c6abc
      VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
Packit 9c6abc
  assert(img != NULL);
Packit 9c6abc
  if (ok) {
Packit 9c6abc
    // Ignore ALPH chunk accompanying VP8L.
Packit 9c6abc
    if (is_lossless && (wpi->alpha_ != NULL)) {
Packit 9c6abc
      ChunkDelete(wpi->alpha_);
Packit 9c6abc
      wpi->alpha_ = NULL;
Packit 9c6abc
    }
Packit 9c6abc
    wpi->width_ = w;
Packit 9c6abc
    wpi->height_ = h;
Packit 9c6abc
    wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
Packit 9c6abc
  }
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
Packit 9c6abc
                         WebPMuxImage* const wpi) {
Packit 9c6abc
  const uint8_t* bytes = chunk->data_.bytes;
Packit 9c6abc
  size_t size = chunk->data_.size;
Packit 9c6abc
  const uint8_t* const last = bytes + size;
Packit 9c6abc
  WebPChunk subchunk;
Packit 9c6abc
  size_t subchunk_size;
Packit 9c6abc
  ChunkInit(&subchunk);
Packit 9c6abc
Packit 9c6abc
  assert(chunk->tag_ == kChunks[IDX_ANMF].tag);
Packit 9c6abc
  assert(!wpi->is_partial_);
Packit 9c6abc
Packit 9c6abc
  // ANMF.
Packit 9c6abc
  {
Packit 9c6abc
    const size_t hdr_size = ANMF_CHUNK_SIZE;
Packit 9c6abc
    const WebPData temp = { bytes, hdr_size };
Packit 9c6abc
    // Each of ANMF chunk contain a header at the beginning. So, its size should
Packit 9c6abc
    // be at least 'hdr_size'.
Packit 9c6abc
    if (size < hdr_size) goto Fail;
Packit 9c6abc
    ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
Packit 9c6abc
  }
Packit 9c6abc
  ChunkSetNth(&subchunk, &wpi->header_, 1);
Packit 9c6abc
  wpi->is_partial_ = 1;  // Waiting for ALPH and/or VP8/VP8L chunks.
Packit 9c6abc
Packit 9c6abc
  // Rest of the chunks.
Packit 9c6abc
  subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
Packit 9c6abc
  bytes += subchunk_size;
Packit 9c6abc
  size -= subchunk_size;
Packit 9c6abc
Packit 9c6abc
  while (bytes != last) {
Packit 9c6abc
    ChunkInit(&subchunk);
Packit 9c6abc
    if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
Packit 9c6abc
                             copy_data) != WEBP_MUX_OK) {
Packit 9c6abc
      goto Fail;
Packit 9c6abc
    }
Packit 9c6abc
    switch (ChunkGetIdFromTag(subchunk.tag_)) {
Packit 9c6abc
      case WEBP_CHUNK_ALPHA:
Packit 9c6abc
        if (wpi->alpha_ != NULL) goto Fail;  // Consecutive ALPH chunks.
Packit 9c6abc
        if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
Packit 9c6abc
        wpi->is_partial_ = 1;  // Waiting for a VP8 chunk.
Packit 9c6abc
        break;
Packit 9c6abc
      case WEBP_CHUNK_IMAGE:
Packit 9c6abc
        if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
Packit 9c6abc
        if (!MuxImageFinalize(wpi)) goto Fail;
Packit 9c6abc
        wpi->is_partial_ = 0;  // wpi is completely filled.
Packit 9c6abc
        break;
Packit 9c6abc
      case WEBP_CHUNK_UNKNOWN:
Packit 9c6abc
        if (wpi->is_partial_) goto Fail;  // Encountered an unknown chunk
Packit 9c6abc
                                          // before some image chunks.
Packit 9c6abc
        if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
Packit 9c6abc
        break;
Packit 9c6abc
      default:
Packit 9c6abc
        goto Fail;
Packit 9c6abc
        break;
Packit 9c6abc
    }
Packit 9c6abc
    subchunk_size = ChunkDiskSize(&subchunk);
Packit 9c6abc
    bytes += subchunk_size;
Packit 9c6abc
    size -= subchunk_size;
Packit 9c6abc
  }
Packit 9c6abc
  if (wpi->is_partial_) goto Fail;
Packit 9c6abc
  return 1;
Packit 9c6abc
Packit 9c6abc
 Fail:
Packit 9c6abc
  ChunkRelease(&subchunk);
Packit 9c6abc
  return 0;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Create a mux object from WebP-RIFF data.
Packit 9c6abc
Packit 9c6abc
WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
Packit 9c6abc
                               int version) {
Packit 9c6abc
  size_t riff_size;
Packit 9c6abc
  uint32_t tag;
Packit 9c6abc
  const uint8_t* end;
Packit 9c6abc
  WebPMux* mux = NULL;
Packit 9c6abc
  WebPMuxImage* wpi = NULL;
Packit 9c6abc
  const uint8_t* data;
Packit 9c6abc
  size_t size;
Packit 9c6abc
  WebPChunk chunk;
Packit 9c6abc
  ChunkInit(&chunk);
Packit 9c6abc
Packit 9c6abc
  // Sanity checks.
Packit 9c6abc
  if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
Packit 9c6abc
    return NULL;  // version mismatch
Packit 9c6abc
  }
Packit 9c6abc
  if (bitstream == NULL) return NULL;
Packit 9c6abc
Packit 9c6abc
  data = bitstream->bytes;
Packit 9c6abc
  size = bitstream->size;
Packit 9c6abc
Packit 9c6abc
  if (data == NULL) return NULL;
Packit 9c6abc
  if (size < RIFF_HEADER_SIZE) return NULL;
Packit 9c6abc
  if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
Packit 9c6abc
      GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
Packit 9c6abc
    return NULL;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  mux = WebPMuxNew();
Packit 9c6abc
  if (mux == NULL) return NULL;
Packit 9c6abc
Packit 9c6abc
  if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err;
Packit 9c6abc
Packit 9c6abc
  tag = GetLE32(data + RIFF_HEADER_SIZE);
Packit 9c6abc
  if (tag != kChunks[IDX_VP8].tag &&
Packit 9c6abc
      tag != kChunks[IDX_VP8L].tag &&
Packit 9c6abc
      tag != kChunks[IDX_VP8X].tag) {
Packit 9c6abc
    goto Err;  // First chunk should be VP8, VP8L or VP8X.
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE));
Packit 9c6abc
  if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) {
Packit 9c6abc
    goto Err;
Packit 9c6abc
  } else {
Packit 9c6abc
    if (riff_size < size) {  // Redundant data after last chunk.
Packit 9c6abc
      size = riff_size;  // To make sure we don't read any data beyond mux_size.
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  end = data + size;
Packit 9c6abc
  data += RIFF_HEADER_SIZE;
Packit 9c6abc
  size -= RIFF_HEADER_SIZE;
Packit 9c6abc
Packit 9c6abc
  wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi));
Packit 9c6abc
  if (wpi == NULL) goto Err;
Packit 9c6abc
  MuxImageInit(wpi);
Packit 9c6abc
Packit 9c6abc
  // Loop over chunks.
Packit 9c6abc
  while (data != end) {
Packit 9c6abc
    size_t data_size;
Packit 9c6abc
    WebPChunkId id;
Packit 9c6abc
    WebPChunk** chunk_list;
Packit 9c6abc
    if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
Packit 9c6abc
                             copy_data) != WEBP_MUX_OK) {
Packit 9c6abc
      goto Err;
Packit 9c6abc
    }
Packit 9c6abc
    data_size = ChunkDiskSize(&chunk);
Packit 9c6abc
    id = ChunkGetIdFromTag(chunk.tag_);
Packit 9c6abc
    switch (id) {
Packit 9c6abc
      case WEBP_CHUNK_ALPHA:
Packit 9c6abc
        if (wpi->alpha_ != NULL) goto Err;  // Consecutive ALPH chunks.
Packit 9c6abc
        if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
Packit 9c6abc
        wpi->is_partial_ = 1;  // Waiting for a VP8 chunk.
Packit 9c6abc
        break;
Packit 9c6abc
      case WEBP_CHUNK_IMAGE:
Packit 9c6abc
        if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
Packit 9c6abc
        if (!MuxImageFinalize(wpi)) goto Err;
Packit 9c6abc
        wpi->is_partial_ = 0;  // wpi is completely filled.
Packit 9c6abc
 PushImage:
Packit 9c6abc
        // Add this to mux->images_ list.
Packit 9c6abc
        if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
Packit 9c6abc
        MuxImageInit(wpi);  // Reset for reading next image.
Packit 9c6abc
        break;
Packit 9c6abc
      case WEBP_CHUNK_ANMF:
Packit 9c6abc
        if (wpi->is_partial_) goto Err;  // Previous wpi is still incomplete.
Packit 9c6abc
        if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
Packit 9c6abc
        ChunkRelease(&chunk);
Packit 9c6abc
        goto PushImage;
Packit 9c6abc
        break;
Packit 9c6abc
      default:  // A non-image chunk.
Packit 9c6abc
        if (wpi->is_partial_) goto Err;  // Encountered a non-image chunk before
Packit 9c6abc
                                         // getting all chunks of an image.
Packit 9c6abc
        chunk_list = MuxGetChunkListFromId(mux, id);  // List to add this chunk.
Packit 9c6abc
        if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
Packit 9c6abc
        if (id == WEBP_CHUNK_VP8X) {  // grab global specs
Packit 9c6abc
          mux->canvas_width_ = GetLE24(data + 12) + 1;
Packit 9c6abc
          mux->canvas_height_ = GetLE24(data + 15) + 1;
Packit 9c6abc
        }
Packit 9c6abc
        break;
Packit 9c6abc
    }
Packit 9c6abc
    data += data_size;
Packit 9c6abc
    size -= data_size;
Packit 9c6abc
    ChunkInit(&chunk);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Incomplete image.
Packit 9c6abc
  if (wpi->is_partial_) goto Err;
Packit 9c6abc
Packit 9c6abc
  // Validate mux if complete.
Packit 9c6abc
  if (MuxValidate(mux) != WEBP_MUX_OK) goto Err;
Packit 9c6abc
Packit 9c6abc
  MuxImageDelete(wpi);
Packit 9c6abc
  return mux;  // All OK;
Packit 9c6abc
Packit 9c6abc
 Err:  // Something bad happened.
Packit 9c6abc
  ChunkRelease(&chunk);
Packit 9c6abc
  MuxImageDelete(wpi);
Packit 9c6abc
  WebPMuxDelete(mux);
Packit 9c6abc
  return NULL;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Get API(s).
Packit 9c6abc
Packit 9c6abc
// Validates that the given mux has a single image.
Packit 9c6abc
static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) {
Packit 9c6abc
  const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
Packit 9c6abc
  const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF);
Packit 9c6abc
Packit 9c6abc
  if (num_images == 0) {
Packit 9c6abc
    // No images in mux.
Packit 9c6abc
    return WEBP_MUX_NOT_FOUND;
Packit 9c6abc
  } else if (num_images == 1 && num_frames == 0) {
Packit 9c6abc
    // Valid case (single image).
Packit 9c6abc
    return WEBP_MUX_OK;
Packit 9c6abc
  } else {
Packit 9c6abc
    // Frame case OR an invalid mux.
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L
Packit 9c6abc
// chunk and canvas size are valid.
Packit 9c6abc
static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
Packit 9c6abc
                                     int* width, int* height, uint32_t* flags) {
Packit 9c6abc
  int w, h;
Packit 9c6abc
  uint32_t f = 0;
Packit 9c6abc
  WebPData data;
Packit 9c6abc
  assert(mux != NULL);
Packit 9c6abc
Packit 9c6abc
  // Check if VP8X chunk is present.
Packit 9c6abc
  if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
Packit 9c6abc
    if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
Packit 9c6abc
    f = GetLE32(data.bytes + 0);
Packit 9c6abc
    w = GetLE24(data.bytes + 4) + 1;
Packit 9c6abc
    h = GetLE24(data.bytes + 7) + 1;
Packit 9c6abc
  } else {
Packit 9c6abc
    const WebPMuxImage* const wpi = mux->images_;
Packit 9c6abc
    // Grab user-forced canvas size as default.
Packit 9c6abc
    w = mux->canvas_width_;
Packit 9c6abc
    h = mux->canvas_height_;
Packit 9c6abc
    if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
Packit 9c6abc
      // single image and not forced canvas size => use dimension of first frame
Packit 9c6abc
      assert(wpi != NULL);
Packit 9c6abc
      w = wpi->width_;
Packit 9c6abc
      h = wpi->height_;
Packit 9c6abc
    }
Packit 9c6abc
    if (wpi != NULL) {
Packit 9c6abc
      if (wpi->has_alpha_) f |= ALPHA_FLAG;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
Packit 9c6abc
Packit 9c6abc
  if (width != NULL) *width = w;
Packit 9c6abc
  if (height != NULL) *height = h;
Packit 9c6abc
  if (flags != NULL) *flags = f;
Packit 9c6abc
  return WEBP_MUX_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, int* width, int* height) {
Packit 9c6abc
  if (mux == NULL || width == NULL || height == NULL) {
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  }
Packit 9c6abc
  return MuxGetCanvasInfo(mux, width, height, NULL);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
Packit 9c6abc
  if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  return MuxGetCanvasInfo(mux, NULL, NULL, flags);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
Packit 9c6abc
                              int height, uint32_t flags) {
Packit 9c6abc
  const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
Packit 9c6abc
  assert(width >= 1 && height >= 1);
Packit 9c6abc
  assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE);
Packit 9c6abc
  assert(width * (uint64_t)height < MAX_IMAGE_AREA);
Packit 9c6abc
  PutLE32(dst, MKFOURCC('V', 'P', '8', 'X'));
Packit 9c6abc
  PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE);
Packit 9c6abc
  PutLE32(dst + CHUNK_HEADER_SIZE, flags);
Packit 9c6abc
  PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1);
Packit 9c6abc
  PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1);
Packit 9c6abc
  return dst + vp8x_size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Assemble a single image WebP bitstream from 'wpi'.
Packit 9c6abc
static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi,
Packit 9c6abc
                                        WebPData* const bitstream) {
Packit 9c6abc
  uint8_t* dst;
Packit 9c6abc
Packit 9c6abc
  // Allocate data.
Packit 9c6abc
  const int need_vp8x = (wpi->alpha_ != NULL);
Packit 9c6abc
  const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0;
Packit 9c6abc
  const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0;
Packit 9c6abc
  // Note: No need to output ANMF chunk for a single image.
Packit 9c6abc
  const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size +
Packit 9c6abc
                      ChunkDiskSize(wpi->img_);
Packit 9c6abc
  uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size);
Packit 9c6abc
  if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
Packit 9c6abc
Packit 9c6abc
  // Main RIFF header.
Packit 9c6abc
  dst = MuxEmitRiffHeader(data, size);
Packit 9c6abc
Packit 9c6abc
  if (need_vp8x) {
Packit 9c6abc
    dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG);  // VP8X.
Packit 9c6abc
    dst = ChunkListEmit(wpi->alpha_, dst);       // ALPH.
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Bitstream.
Packit 9c6abc
  dst = ChunkListEmit(wpi->img_, dst);
Packit 9c6abc
  assert(dst == data + size);
Packit 9c6abc
Packit 9c6abc
  // Output.
Packit 9c6abc
  bitstream->bytes = data;
Packit 9c6abc
  bitstream->size = size;
Packit 9c6abc
  return WEBP_MUX_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
Packit 9c6abc
                             WebPData* chunk_data) {
Packit 9c6abc
  CHUNK_INDEX idx;
Packit 9c6abc
  if (mux == NULL || fourcc == NULL || chunk_data == NULL) {
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  }
Packit 9c6abc
  idx = ChunkGetIndexFromFourCC(fourcc);
Packit 9c6abc
  if (IsWPI(kChunks[idx].id)) {     // An image chunk.
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  } else if (idx != IDX_UNKNOWN) {  // A known chunk type.
Packit 9c6abc
    return MuxGet(mux, idx, 1, chunk_data);
Packit 9c6abc
  } else {                          // An unknown chunk type.
Packit 9c6abc
    const WebPChunk* const chunk =
Packit 9c6abc
        ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc));
Packit 9c6abc
    if (chunk == NULL) return WEBP_MUX_NOT_FOUND;
Packit 9c6abc
    *chunk_data = chunk->data_;
Packit 9c6abc
    return WEBP_MUX_OK;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi,
Packit 9c6abc
                                        WebPMuxFrameInfo* const info) {
Packit 9c6abc
  // Set some defaults for unrelated fields.
Packit 9c6abc
  info->x_offset = 0;
Packit 9c6abc
  info->y_offset = 0;
Packit 9c6abc
  info->duration = 1;
Packit 9c6abc
  info->dispose_method = WEBP_MUX_DISPOSE_NONE;
Packit 9c6abc
  info->blend_method = WEBP_MUX_BLEND;
Packit 9c6abc
  // Extract data for related fields.
Packit 9c6abc
  info->id = ChunkGetIdFromTag(wpi->img_->tag_);
Packit 9c6abc
  return SynthesizeBitstream(wpi, &info->bitstream);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi,
Packit 9c6abc
                                        WebPMuxFrameInfo* const frame) {
Packit 9c6abc
  const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag);
Packit 9c6abc
  const WebPData* frame_data;
Packit 9c6abc
  if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  assert(wpi->header_ != NULL);  // Already checked by WebPMuxGetFrame().
Packit 9c6abc
  // Get frame chunk.
Packit 9c6abc
  frame_data = &wpi->header_->data_;
Packit 9c6abc
  if (frame_data->size < kChunks[IDX_ANMF].size) return WEBP_MUX_BAD_DATA;
Packit 9c6abc
  // Extract info.
Packit 9c6abc
  frame->x_offset = 2 * GetLE24(frame_data->bytes + 0);
Packit 9c6abc
  frame->y_offset = 2 * GetLE24(frame_data->bytes + 3);
Packit 9c6abc
  {
Packit 9c6abc
    const uint8_t bits = frame_data->bytes[15];
Packit 9c6abc
    frame->duration = GetLE24(frame_data->bytes + 12);
Packit 9c6abc
    frame->dispose_method =
Packit 9c6abc
        (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
Packit 9c6abc
    frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
Packit 9c6abc
  }
Packit 9c6abc
  frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
Packit 9c6abc
  return SynthesizeBitstream(wpi, &frame->bitstream);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxGetFrame(
Packit 9c6abc
    const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) {
Packit 9c6abc
  WebPMuxError err;
Packit 9c6abc
  WebPMuxImage* wpi;
Packit 9c6abc
Packit 9c6abc
  // Sanity checks.
Packit 9c6abc
  if (mux == NULL || frame == NULL) {
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // Get the nth WebPMuxImage.
Packit 9c6abc
  err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi;;
Packit 9c6abc
  if (err != WEBP_MUX_OK) return err;
Packit 9c6abc
Packit 9c6abc
  // Get frame info.
Packit 9c6abc
  if (wpi->header_ == NULL) {
Packit 9c6abc
    return MuxGetImageInternal(wpi, frame);
Packit 9c6abc
  } else {
Packit 9c6abc
    return MuxGetFrameInternal(wpi, frame);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux,
Packit 9c6abc
                                       WebPMuxAnimParams* params) {
Packit 9c6abc
  WebPData anim;
Packit 9c6abc
  WebPMuxError err;
Packit 9c6abc
Packit 9c6abc
  if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
Packit 9c6abc
  err = MuxGet(mux, IDX_ANIM, 1, &anim);
Packit 9c6abc
  if (err != WEBP_MUX_OK) return err;
Packit 9c6abc
  if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA;
Packit 9c6abc
  params->bgcolor = GetLE32(anim.bytes);
Packit 9c6abc
  params->loop_count = GetLE16(anim.bytes + 4);
Packit 9c6abc
Packit 9c6abc
  return WEBP_MUX_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Get chunk index from chunk id. Returns IDX_NIL if not found.
Packit 9c6abc
static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) {
Packit 9c6abc
  int i;
Packit 9c6abc
  for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) {
Packit 9c6abc
    if (id == kChunks[i].id) return (CHUNK_INDEX)i;
Packit 9c6abc
  }
Packit 9c6abc
  return IDX_NIL;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Count number of chunks matching 'tag' in the 'chunk_list'.
Packit 9c6abc
// If tag == NIL_TAG, any tag will be matched.
Packit 9c6abc
static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) {
Packit 9c6abc
  int count = 0;
Packit 9c6abc
  const WebPChunk* current;
Packit 9c6abc
  for (current = chunk_list; current != NULL; current = current->next_) {
Packit 9c6abc
    if (tag == NIL_TAG || current->tag_ == tag) {
Packit 9c6abc
      count++;  // Count chunks whose tags match.
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return count;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
Packit 9c6abc
                              WebPChunkId id, int* num_elements) {
Packit 9c6abc
  if (mux == NULL || num_elements == NULL) {
Packit 9c6abc
    return WEBP_MUX_INVALID_ARGUMENT;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (IsWPI(id)) {
Packit 9c6abc
    *num_elements = MuxImageCount(mux->images_, id);
Packit 9c6abc
  } else {
Packit 9c6abc
    WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
Packit 9c6abc
    const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
Packit 9c6abc
    *num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  return WEBP_MUX_OK;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------