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