// Create data for frame/tile given image data, offsets and duration. static WebPMuxError CreateFrameTileData(const WebPData* const image, int x_offset, int y_offset, int duration, int is_lossless, int is_frame, WebPData* const frame_tile) { int width; int height; uint8_t* frame_tile_bytes; const size_t frame_tile_size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size; const int ok = is_lossless ? VP8LGetInfo(image->bytes_, image->size_, &width, &height, NULL) : VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height); if (!ok) return WEBP_MUX_INVALID_ARGUMENT; assert(width > 0 && height > 0 && duration > 0); // Note: assertion on upper bounds is done in PutLE24(). frame_tile_bytes = (uint8_t*)malloc(frame_tile_size); if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_tile_bytes + 0, x_offset / 2); PutLE24(frame_tile_bytes + 3, y_offset / 2); if (is_frame) { PutLE24(frame_tile_bytes + 6, width - 1); PutLE24(frame_tile_bytes + 9, height - 1); PutLE24(frame_tile_bytes + 12, duration - 1); } frame_tile->bytes_ = frame_tile_bytes; frame_tile->size_ = frame_tile_size; return WEBP_MUX_OK; }
// Create data for frame/fragment given image data, offsets and duration. static WebPMuxError CreateFrameFragmentData( const WebPData* const image, int x_offset, int y_offset, int duration, WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame, WebPData* const frame_frgm) { int width; int height; uint8_t* frame_frgm_bytes; const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; const int ok = is_lossless ? VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) : VP8GetInfo(image->bytes, image->size, image->size, &width, &height); if (!ok) return WEBP_MUX_INVALID_ARGUMENT; assert(width > 0 && height > 0 && duration >= 0); assert(dispose_method == (dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size); if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_frgm_bytes + 0, x_offset / 2); PutLE24(frame_frgm_bytes + 3, y_offset / 2); if (is_frame) { PutLE24(frame_frgm_bytes + 6, width - 1); PutLE24(frame_frgm_bytes + 9, height - 1); PutLE24(frame_frgm_bytes + 12, duration); frame_frgm_bytes[15] = (dispose_method & 1); } frame_frgm->bytes = frame_frgm_bytes; frame_frgm->size = frame_frgm_size; return WEBP_MUX_OK; }
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk, int* const width, int* const height) { const uint32_t tag = image_chunk->tag_; const WebPData* const data = &image_chunk->data_; int w, h; int ok; assert(image_chunk != NULL); assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag); ok = (tag == kChunks[IDX_VP8].tag) ? VP8GetInfo(data->bytes_, data->size_, data->size_, &w, &h) : VP8LGetInfo(data->bytes_, data->size_, &w, &h, NULL); if (ok) { *width = w; *height = h; return WEBP_MUX_OK; } else { return WEBP_MUX_BAD_DATA; } }
int MuxImageFinalize(WebPMuxImage* const wpi) { const WebPChunk* const img = wpi->img_; const WebPData* const image = &img->data_; const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag); int w, h; int vp8l_has_alpha = 0; const int ok = is_lossless ? VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) : VP8GetInfo(image->bytes, image->size, image->size, &w, &h); assert(img != NULL); if (ok) { // Ignore ALPH chunk accompanying VP8L. if (is_lossless && (wpi->alpha_ != NULL)) { ChunkDelete(wpi->alpha_); wpi->alpha_ = NULL; } wpi->width_ = w; wpi->height_ = h; wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL); } return ok; }
WebPMuxError MuxGetImageInfo(const WebPChunk* const image_chunk, int* const width, int* const height, int* const has_alpha) { const uint32_t tag = image_chunk->tag_; const WebPData* const data = &image_chunk->data_; int w, h; int a = 0; int ok; assert(image_chunk != NULL); assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag); ok = (tag == kChunks[IDX_VP8].tag) ? VP8GetInfo(data->bytes, data->size, data->size, &w, &h) : VP8LGetInfo(data->bytes, data->size, &w, &h, &a); if (ok) { if (width != NULL) *width = w; if (height != NULL) *height = h; if (has_alpha != NULL) *has_alpha = a; return WEBP_MUX_OK; } else { return WEBP_MUX_BAD_DATA; } }
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the // minimal amount will be read to fetch the remaining parameters. // If 'headers' is non-NULL this function will attempt to locate both alpha // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). // Note: The following chunk sequences (before the raw VP8/VP8L data) are // considered valid by this function: // RIFF + VP8(L) // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, int* const width, int* const height, int* const has_alpha, int* const has_animation, int* const format, WebPHeaderStructure* const headers) { int canvas_width = 0; int canvas_height = 0; int image_width = 0; int image_height = 0; int found_riff = 0; int found_vp8x = 0; int animation_present = 0; int fragments_present = 0; const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; VP8StatusCode status; WebPHeaderStructure hdrs; if (data == NULL || data_size < RIFF_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } memset(&hdrs, 0, sizeof(hdrs)); hdrs.data = data; hdrs.data_size = data_size; // Skip over RIFF header. status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size); if (status != VP8_STATUS_OK) { return status; // Wrong RIFF header / insufficient data. } found_riff = (hdrs.riff_size > 0); // Skip over VP8X. { uint32_t flags = 0; status = ParseVP8X(&data, &data_size, &found_vp8x, &canvas_width, &canvas_height, &flags); if (status != VP8_STATUS_OK) { return status; // Wrong VP8X / insufficient data. } animation_present = !!(flags & ANIMATION_FLAG); fragments_present = !!(flags & FRAGMENTS_FLAG); if (!found_riff && found_vp8x) { // Note: This restriction may be removed in the future, if it becomes // necessary to send VP8X chunk to the decoder. return VP8_STATUS_BITSTREAM_ERROR; } if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); if (has_animation != NULL) *has_animation = animation_present; if (format != NULL) *format = 0; // default = undefined image_width = canvas_width; image_height = canvas_height; if (found_vp8x && (animation_present || fragments_present) && headers == NULL) { status = VP8_STATUS_OK; goto ReturnWidthHeight; // Just return features from VP8X header. } } if (data_size < TAG_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". if ((found_riff && found_vp8x) || (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, &hdrs.alpha_data, &hdrs.alpha_data_size); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Invalid chunk size / insufficient data. } } // Skip over VP8/VP8L header. status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size, &hdrs.compressed_size, &hdrs.is_lossless); if (status != VP8_STATUS_OK) { goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data. } if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (format != NULL && !(animation_present || fragments_present)) { *format = hdrs.is_lossless ? 2 : 1; } if (!hdrs.is_lossless) { if (data_size < VP8_FRAME_HEADER_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Validates raw VP8 data. if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, &image_width, &image_height)) { return VP8_STATUS_BITSTREAM_ERROR; } } else { if (data_size < VP8L_FRAME_HEADER_SIZE) { status = VP8_STATUS_NOT_ENOUGH_DATA; goto ReturnWidthHeight; } // Validates raw VP8L data. if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { return VP8_STATUS_BITSTREAM_ERROR; } } // Validates image size coherency. if (found_vp8x) { if (canvas_width != image_width || canvas_height != image_height) { return VP8_STATUS_BITSTREAM_ERROR; } } if (headers != NULL) { *headers = hdrs; headers->offset = data - headers->data; assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); assert(headers->offset == headers->data_size - data_size); } ReturnWidthHeight: if (status == VP8_STATUS_OK || (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) { if (has_alpha != NULL) { // If the data did not contain a VP8X/VP8L chunk the only definitive way // to set this is by looking for alpha data (from an ALPH chunk). *has_alpha |= (hdrs.alpha_data != NULL); } if (width != NULL) *width = image_width; if (height != NULL) *height = image_height; return VP8_STATUS_OK; } else { return status; } }
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on // 'data'. All the output parameters may be NULL. If 'headers' is NULL only the // minimal amount will be read to fetch the remaining parameters. // If 'headers' is non-NULL this function will attempt to locate both alpha // data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L). // Note: The following chunk sequences (before the raw VP8/VP8L data) are // considered valid by this function: // RIFF + VP8(L) // RIFF + VP8X + (optional chunks) + VP8(L) // ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose. // VP8(L) <-- Not a valid WebP format: only allowed for internal purpose. static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, int* const width, int* const height, int* const has_alpha, WebPHeaderStructure* const headers) { int found_riff = 0; int found_vp8x = 0; VP8StatusCode status; WebPHeaderStructure hdrs; if (data == NULL || data_size < RIFF_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } memset(&hdrs, 0, sizeof(hdrs)); hdrs.data = data; hdrs.data_size = data_size; // Skip over RIFF header. status = ParseRIFF(&data, &data_size, &hdrs.riff_size); if (status != VP8_STATUS_OK) { return status; // Wrong RIFF header / insufficient data. } found_riff = (hdrs.riff_size > 0); // Skip over VP8X. { uint32_t flags = 0; status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags); if (status != VP8_STATUS_OK) { return status; // Wrong VP8X / insufficient data. } if (!found_riff && found_vp8x) { // Note: This restriction may be removed in the future, if it becomes // necessary to send VP8X chunk to the decoder. return VP8_STATUS_BITSTREAM_ERROR; } if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG_BIT); if (found_vp8x && headers == NULL) { return VP8_STATUS_OK; // Return features from VP8X header. } } if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA; // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH". if ((found_riff && found_vp8x) || (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, &hdrs.alpha_data, &hdrs.alpha_data_size); if (status != VP8_STATUS_OK) { return status; // Found an invalid chunk size / insufficient data. } } // Skip over VP8/VP8L header. status = ParseVP8Header(&data, &data_size, hdrs.riff_size, &hdrs.compressed_size, &hdrs.is_lossless); if (status != VP8_STATUS_OK) { return status; // Wrong VP8/VP8L chunk-header / insufficient data. } if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (!hdrs.is_lossless) { if (data_size < VP8_FRAME_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } // Validates raw VP8 data. if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, width, height)) { return VP8_STATUS_BITSTREAM_ERROR; } } else { if (data_size < VP8L_FRAME_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } // Validates raw VP8L data. if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) { return VP8_STATUS_BITSTREAM_ERROR; } } if (has_alpha != NULL) { // If the data did not contain a VP8X/VP8L chunk the only definitive way // to set this is by looking for alpha data (from an ALPH chunk). *has_alpha |= (hdrs.alpha_data != NULL); } if (headers != NULL) { *headers = hdrs; headers->offset = data - headers->data; assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); assert(headers->offset == headers->data_size - data_size); } return VP8_STATUS_OK; // Return features from VP8 header. }
static VP8StatusCode ParseHeadersInternal(const uint8_t* data, size_t data_size, int* const width, int* const height, int* const has_alpha, int* const has_animation, WebPHeaderStructure* const headers) { int canvas_width = 0; int canvas_height = 0; int image_width = 0; int image_height = 0; int found_riff = 0; int found_vp8x = 0; VP8StatusCode status; WebPHeaderStructure hdrs; if (data == NULL || data_size < RIFF_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } memset(&hdrs, 0, sizeof(hdrs)); hdrs.data = data; hdrs.data_size = data_size; status = ParseRIFF(&data, &data_size, &hdrs.riff_size); if (status != VP8_STATUS_OK) { return status; } found_riff = (hdrs.riff_size > 0); { uint32_t flags = 0; int animation_present; status = ParseVP8X(&data, &data_size, &found_vp8x, &canvas_width, &canvas_height, &flags); if (status != VP8_STATUS_OK) { return status; } animation_present = !!(flags & ANIMATION_FLAG); if (!found_riff && found_vp8x) { return VP8_STATUS_BITSTREAM_ERROR; } if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG); if (has_animation != NULL) *has_animation = animation_present; if (found_vp8x && animation_present && headers == NULL) { if (width != NULL) *width = canvas_width; if (height != NULL) *height = canvas_height; return VP8_STATUS_OK; } } if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA; if ((found_riff && found_vp8x) || (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) { status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size, &hdrs.alpha_data, &hdrs.alpha_data_size); if (status != VP8_STATUS_OK) { return status; } } status = ParseVP8Header(&data, &data_size, hdrs.riff_size, &hdrs.compressed_size, &hdrs.is_lossless); if (status != VP8_STATUS_OK) { return status; } if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) { return VP8_STATUS_BITSTREAM_ERROR; } if (!hdrs.is_lossless) { if (data_size < VP8_FRAME_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size, &image_width, &image_height)) { return VP8_STATUS_BITSTREAM_ERROR; } } else { if (data_size < VP8L_FRAME_HEADER_SIZE) { return VP8_STATUS_NOT_ENOUGH_DATA; } if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) { return VP8_STATUS_BITSTREAM_ERROR; } } if (found_vp8x) { if (canvas_width != image_width || canvas_height != image_height) { return VP8_STATUS_BITSTREAM_ERROR; } } if (width != NULL) *width = image_width; if (height != NULL) *height = image_height; if (has_alpha != NULL) { *has_alpha |= (hdrs.alpha_data != NULL); } if (headers != NULL) { *headers = hdrs; headers->offset = data - headers->data; assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD); assert(headers->offset == headers->data_size - data_size); } return VP8_STATUS_OK; }