mirror of
https://github.com/webmproject/libwebp.git
synced 2025-07-14 21:09:55 +02:00
Mux API change:
'Set' and 'Get' methods for images take/return a bitstream as input, instead of separate 'image' and 'alpha' arguments. Also, - Make WebPDataCopy() a public API - Use WebPData for storing data in WebPChunk. - Fix a potential memleak. Change-Id: I4bf5ee6b39971384cb124b5b43921c27e9aabf3e
This commit is contained in:
@ -167,27 +167,32 @@ static WebPMuxError CreateDataFromImageInfo(const WebPImageInfo* image_info,
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
// Outputs image data given data from a webp file (including RIFF header).
|
||||
// Outputs image data given a bitstream. The bitstream can either be a
|
||||
// single-image WebP file or raw VP8/VP8L data.
|
||||
// Also outputs 'is_lossless' to be true if the given bitstream is lossless.
|
||||
static WebPMuxError GetImageData(const WebPData* const bitstream,
|
||||
WebPData* const image, WebPData* const alpha,
|
||||
int* const is_lossless) {
|
||||
memset(alpha, 0, sizeof(*alpha)); // Default: no alpha.
|
||||
if (bitstream->size_ < TAG_SIZE ||
|
||||
memcmp(bitstream->bytes_, "RIFF", TAG_SIZE)) {
|
||||
// It is NOT webp file data. Return input data as is.
|
||||
*image = *bitstream;
|
||||
*is_lossless = VP8LCheckSignature(image->bytes_, image->size_);
|
||||
return WEBP_MUX_OK;
|
||||
} else {
|
||||
// It is webp file data. Extract image data from it.
|
||||
WebPMuxError err;
|
||||
const WebPMuxImage* wpi;
|
||||
WebPMux* const mux = WebPMuxCreate(bitstream, 0);
|
||||
if (mux == NULL) return WEBP_MUX_BAD_DATA;
|
||||
err = WebPMuxGetImage(mux, image, alpha);
|
||||
wpi = mux->images_;
|
||||
assert(wpi != NULL && wpi->img_ != NULL);
|
||||
*image = wpi->img_->data_;
|
||||
if (wpi->alpha_ != NULL) {
|
||||
*alpha = wpi->alpha_->data_;
|
||||
}
|
||||
WebPMuxDelete(mux);
|
||||
*is_lossless = VP8LCheckSignature(image->bytes_, image->size_);
|
||||
return err;
|
||||
}
|
||||
*is_lossless = VP8LCheckSignature(image->bytes_, image->size_);
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) {
|
||||
@ -226,25 +231,23 @@ static WebPMuxError DeleteLoopCount(WebPMux* const mux) {
|
||||
// Set API(s).
|
||||
|
||||
WebPMuxError WebPMuxSetImage(WebPMux* const mux,
|
||||
const WebPData* const image,
|
||||
const WebPData* const alpha,
|
||||
int copy_data) {
|
||||
const WebPData* const bitstream, int copy_data) {
|
||||
WebPMuxError err;
|
||||
WebPChunk chunk;
|
||||
WebPMuxImage wpi;
|
||||
WebPData image_raw;
|
||||
const int has_alpha = (alpha != NULL && alpha->bytes_ != NULL);
|
||||
WebPData image;
|
||||
WebPData alpha;
|
||||
int is_lossless;
|
||||
int image_tag;
|
||||
|
||||
if (mux == NULL || image == NULL || image->bytes_ == NULL ||
|
||||
image->size_ > MAX_CHUNK_PAYLOAD) {
|
||||
if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL ||
|
||||
bitstream->size_ > MAX_CHUNK_PAYLOAD) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// If given data is for a whole webp file,
|
||||
// extract only the VP8/VP8L data from it.
|
||||
err = GetImageData(image, &image_raw, NULL, &is_lossless);
|
||||
err = GetImageData(bitstream, &image, &alpha, &is_lossless);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
|
||||
|
||||
@ -253,25 +256,34 @@ WebPMuxError WebPMuxSetImage(WebPMux* const mux,
|
||||
|
||||
MuxImageInit(&wpi);
|
||||
|
||||
if (has_alpha) { // Add alpha chunk.
|
||||
if (alpha.bytes_ != NULL) { // Add alpha chunk.
|
||||
ChunkInit(&chunk);
|
||||
err = ChunkAssignDataImageInfo(&chunk, alpha, NULL, copy_data,
|
||||
err = ChunkAssignDataImageInfo(&chunk, &alpha, NULL, copy_data,
|
||||
kChunks[IDX_ALPHA].tag);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
err = ChunkSetNth(&chunk, &wpi.alpha_, 1);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
}
|
||||
|
||||
// Add image chunk.
|
||||
ChunkInit(&chunk);
|
||||
err = ChunkAssignDataImageInfo(&chunk, &image_raw, NULL, copy_data,
|
||||
image_tag);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
err = ChunkAssignDataImageInfo(&chunk, &image, NULL, copy_data, image_tag);
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
err = ChunkSetNth(&chunk, &wpi.img_, 1);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
|
||||
// Add this image to mux.
|
||||
return MuxImageSetNth(&wpi, &mux->images_, 1);
|
||||
err = MuxImageSetNth(&wpi, &mux->images_, 1);
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
|
||||
// All OK.
|
||||
return WEBP_MUX_OK;
|
||||
|
||||
Err:
|
||||
// Something bad happened.
|
||||
ChunkRelease(&chunk);
|
||||
MuxImageRelease(&wpi);
|
||||
return err;
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxSetMetadata(WebPMux* const mux,
|
||||
@ -332,12 +344,12 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) {
|
||||
}
|
||||
|
||||
static WebPMuxError MuxSetFrameTileInternal(
|
||||
WebPMux* const mux, uint32_t nth,
|
||||
const WebPData* const image, const WebPData* const alpha,
|
||||
WebPMux* const mux, uint32_t nth, const WebPData* const bitstream,
|
||||
uint32_t x_offset, uint32_t y_offset, uint32_t duration,
|
||||
int copy_data, uint32_t tag) {
|
||||
WebPChunk chunk;
|
||||
WebPData image_raw;
|
||||
WebPData image;
|
||||
WebPData alpha;
|
||||
WebPMuxImage wpi;
|
||||
WebPMuxError err;
|
||||
WebPImageInfo* image_info = NULL;
|
||||
@ -345,44 +357,43 @@ static WebPMuxError MuxSetFrameTileInternal(
|
||||
size_t frame_tile_size = 0;
|
||||
WebPData frame_tile;
|
||||
const int is_frame = (tag == kChunks[IDX_FRAME].tag) ? 1 : 0;
|
||||
const int has_alpha = (alpha != NULL && alpha->bytes_ != NULL);
|
||||
int is_lossless;
|
||||
int image_tag;
|
||||
|
||||
if (mux == NULL || image == NULL || image->bytes_ == NULL ||
|
||||
image->size_ > MAX_CHUNK_PAYLOAD) {
|
||||
if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL ||
|
||||
bitstream->size_ > MAX_CHUNK_PAYLOAD) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// If given data is for a whole webp file,
|
||||
// extract only the VP8/VP8L data from it.
|
||||
err = GetImageData(image, &image_raw, NULL, &is_lossless);
|
||||
err = GetImageData(bitstream, &image, &alpha, &is_lossless);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
|
||||
|
||||
ChunkInit(&chunk);
|
||||
MuxImageInit(&wpi);
|
||||
|
||||
if (has_alpha) {
|
||||
if (alpha.bytes_ != NULL) {
|
||||
// Add alpha chunk.
|
||||
err = ChunkAssignDataImageInfo(&chunk, alpha, NULL, copy_data,
|
||||
err = ChunkAssignDataImageInfo(&chunk, &alpha, NULL, copy_data,
|
||||
kChunks[IDX_ALPHA].tag);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
err = ChunkSetNth(&chunk, &wpi.alpha_, 1);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
ChunkInit(&chunk); // chunk owned by wpi.alpha_ now.
|
||||
}
|
||||
|
||||
// Create image_info object.
|
||||
image_info = CreateImageInfo(x_offset, y_offset, duration, &image_raw,
|
||||
image_info = CreateImageInfo(x_offset, y_offset, duration, &image,
|
||||
is_lossless);
|
||||
if (image_info == NULL) {
|
||||
MuxImageRelease(&wpi);
|
||||
return WEBP_MUX_MEMORY_ERROR;
|
||||
err = WEBP_MUX_MEMORY_ERROR;
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// Add image chunk.
|
||||
err = ChunkAssignDataImageInfo(&chunk, &image_raw, image_info, copy_data,
|
||||
err = ChunkAssignDataImageInfo(&chunk, &image, image_info, copy_data,
|
||||
image_tag);
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
image_info = NULL; // Owned by 'chunk' now.
|
||||
@ -424,23 +435,20 @@ static WebPMuxError MuxSetFrameTileInternal(
|
||||
// TODO(urvang): Think about whether we need 'nth' while adding a frame or tile.
|
||||
|
||||
WebPMuxError WebPMuxSetFrame(WebPMux* const mux, uint32_t nth,
|
||||
const WebPData* const image,
|
||||
const WebPData* const alpha,
|
||||
const WebPData* const bitstream,
|
||||
uint32_t x_offset, uint32_t y_offset,
|
||||
uint32_t duration, int copy_data) {
|
||||
return MuxSetFrameTileInternal(mux, nth, image, alpha,
|
||||
x_offset, y_offset, duration,
|
||||
copy_data, kChunks[IDX_FRAME].tag);
|
||||
return MuxSetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset,
|
||||
duration, copy_data, kChunks[IDX_FRAME].tag);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxSetTile(WebPMux* const mux, uint32_t nth,
|
||||
const WebPData* const image,
|
||||
const WebPData* const alpha,
|
||||
const WebPData* const bitstream,
|
||||
uint32_t x_offset, uint32_t y_offset,
|
||||
int copy_data) {
|
||||
return MuxSetFrameTileInternal(mux, nth, image, alpha,
|
||||
x_offset, y_offset, 1,
|
||||
copy_data, kChunks[IDX_TILE].tag);
|
||||
return MuxSetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset,
|
||||
1 /* unused duration*/, copy_data,
|
||||
kChunks[IDX_TILE].tag);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -487,17 +495,17 @@ WebPMuxError WebPMuxDeleteTile(WebPMux* const mux, uint32_t nth) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Assembly of the WebP RIFF file.
|
||||
|
||||
static WebPMuxError GetImageWidthHeight(const WebPChunk* const image_chunk,
|
||||
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(image_chunk->data_, image_chunk->payload_size_,
|
||||
image_chunk->payload_size_, &w, &h) :
|
||||
VP8LGetInfo(image_chunk->data_, image_chunk->payload_size_, &w, &h, NULL);
|
||||
VP8GetInfo(data->bytes_, data->size_, data->size_, &w, &h) :
|
||||
VP8LGetInfo(data->bytes_, data->size_, &w, &h, NULL);
|
||||
if (ok) {
|
||||
*width = w;
|
||||
*height = h;
|
||||
@ -555,7 +563,7 @@ static WebPMuxError GetImageCanvasWidthHeight(
|
||||
// For a single image, extract the width & height from VP8/VP8L image-data.
|
||||
int w, h;
|
||||
const WebPChunk* const image_chunk = wpi->img_;
|
||||
const WebPMuxError err = GetImageWidthHeight(image_chunk, &w, &h);
|
||||
const WebPMuxError err = MuxGetImageWidthHeight(image_chunk, &w, &h);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
*width = w;
|
||||
*height = h;
|
||||
@ -579,7 +587,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||
|
||||
assert(mux != NULL);
|
||||
images = mux->images_; // First image.
|
||||
if (images == NULL || images->img_ == NULL || images->img_->data_ == NULL) {
|
||||
if (images == NULL || images->img_ == NULL ||
|
||||
images->img_->data_.bytes_ == NULL) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -589,11 +598,11 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
||||
|
||||
// Set flags.
|
||||
if (mux->iccp_ != NULL && mux->iccp_->data_ != NULL) {
|
||||
if (mux->iccp_ != NULL && mux->iccp_->data_.bytes_ != NULL) {
|
||||
flags |= ICCP_FLAG;
|
||||
}
|
||||
|
||||
if (mux->meta_ != NULL && mux->meta_->data_ != NULL) {
|
||||
if (mux->meta_ != NULL && mux->meta_->data_.bytes_ != NULL) {
|
||||
flags |= META_FLAG;
|
||||
}
|
||||
|
||||
@ -607,7 +616,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||
}
|
||||
}
|
||||
|
||||
if (images->alpha_ != NULL && images->alpha_->data_ != NULL) {
|
||||
if (images->alpha_ != NULL && images->alpha_->data_.bytes_ != NULL) {
|
||||
// This is an image with alpha channel.
|
||||
flags |= ALPHA_FLAG;
|
||||
}
|
||||
@ -674,14 +683,8 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux,
|
||||
data = (uint8_t*)malloc(size);
|
||||
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
|
||||
// Main RIFF header.
|
||||
PutLE32(data + 0, mktag('R', 'I', 'F', 'F'));
|
||||
PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
|
||||
assert(size == (uint32_t)size);
|
||||
PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, mktag('W', 'E', 'B', 'P'));
|
||||
|
||||
// Chunks.
|
||||
dst = data + RIFF_HEADER_SIZE;
|
||||
// Emit header & chunks.
|
||||
dst = MuxEmitRiffHeader(data, size);
|
||||
dst = ChunkListEmit(mux->vp8x_, dst);
|
||||
dst = ChunkListEmit(mux->iccp_, dst);
|
||||
dst = ChunkListEmit(mux->loop_, dst);
|
||||
|
@ -38,12 +38,11 @@ typedef struct {
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
struct WebPChunk {
|
||||
uint32_t tag_;
|
||||
size_t payload_size_;
|
||||
WebPImageInfo* image_info_;
|
||||
int owner_; // True if *data_ memory is owned internally.
|
||||
// VP8X, Loop, and other internally created chunks
|
||||
// like frame/tile are always owned.
|
||||
const uint8_t* data_;
|
||||
WebPData data_;
|
||||
WebPChunk* next_;
|
||||
};
|
||||
|
||||
@ -158,8 +157,9 @@ WebPChunk* ChunkDelete(WebPChunk* const chunk);
|
||||
|
||||
// Size of a chunk including header and padding.
|
||||
static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
|
||||
assert(chunk->payload_size_ < MAX_CHUNK_PAYLOAD);
|
||||
return SizeWithPadding(chunk->payload_size_);
|
||||
const size_t data_size = chunk->data_.size_;
|
||||
assert(data_size < MAX_CHUNK_PAYLOAD);
|
||||
return SizeWithPadding(data_size);
|
||||
}
|
||||
|
||||
// Total size of a list of chunks.
|
||||
@ -168,6 +168,10 @@ size_t ChunksListDiskSize(const WebPChunk* chunk_list);
|
||||
// Write out the given list of chunks into 'dst'.
|
||||
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
|
||||
|
||||
// Get the width & height of image stored in 'image_chunk'.
|
||||
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk,
|
||||
int* const width, int* const height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MuxImage object management.
|
||||
|
||||
@ -224,17 +228,26 @@ WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id, WebPMuxImage** wpi);
|
||||
|
||||
// Total size of the given image.
|
||||
size_t MuxImageDiskSize(const WebPMuxImage* const wpi);
|
||||
|
||||
// Total size of a list of images.
|
||||
size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list);
|
||||
|
||||
// Write out the given image into 'dst'.
|
||||
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst);
|
||||
|
||||
// Write out the given list of images into 'dst'.
|
||||
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
|
||||
// Checks if the given image list contains at least one lossless image.
|
||||
int MuxHasLosslessImages(const WebPMuxImage* images);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
// Write out RIFF header into 'data', given total data size 'size'.
|
||||
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
|
||||
|
||||
// Returns the list where chunk with given ID is to be inserted in mux.
|
||||
// Return value is NULL if this chunk should be inserted in mux->images_ list
|
||||
|
@ -48,7 +48,7 @@ WebPChunk* ChunkRelease(WebPChunk* const chunk) {
|
||||
if (chunk == NULL) return NULL;
|
||||
free(chunk->image_info_);
|
||||
if (chunk->owner_) {
|
||||
free((void*)chunk->data_);
|
||||
WebPDataClear(&chunk->data_);
|
||||
}
|
||||
next = chunk->next_;
|
||||
ChunkInit(chunk);
|
||||
@ -136,17 +136,16 @@ WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk,
|
||||
if (data != NULL) {
|
||||
if (copy_data) {
|
||||
// Copy data.
|
||||
chunk->data_ = (uint8_t*)malloc(data->size_);
|
||||
if (chunk->data_ == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
memcpy((uint8_t*)chunk->data_, data->bytes_, data->size_);
|
||||
chunk->payload_size_ = data->size_;
|
||||
chunk->data_.bytes_ = (uint8_t*)malloc(data->size_);
|
||||
if (chunk->data_.bytes_ == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
memcpy((uint8_t*)chunk->data_.bytes_, data->bytes_, data->size_);
|
||||
chunk->data_.size_ = data->size_;
|
||||
|
||||
// Chunk is owner of data.
|
||||
chunk->owner_ = 1;
|
||||
} else {
|
||||
// Don't copy data.
|
||||
chunk->data_ = data->bytes_;
|
||||
chunk->payload_size_ = data->size_;
|
||||
chunk->data_ = *data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,14 +196,15 @@ size_t ChunksListDiskSize(const WebPChunk* chunk_list) {
|
||||
}
|
||||
|
||||
static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
|
||||
const size_t chunk_size = chunk->data_.size_;
|
||||
assert(chunk);
|
||||
assert(chunk->tag_ != NIL_TAG);
|
||||
PutLE32(dst + 0, chunk->tag_);
|
||||
PutLE32(dst + TAG_SIZE, (uint32_t)chunk->payload_size_);
|
||||
assert(chunk->payload_size_ == (uint32_t)chunk->payload_size_);
|
||||
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_, chunk->payload_size_);
|
||||
if (chunk->payload_size_ & 1)
|
||||
dst[CHUNK_HEADER_SIZE + chunk->payload_size_] = 0; // Add padding.
|
||||
PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
|
||||
assert(chunk_size == (uint32_t)chunk_size);
|
||||
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes_, chunk_size);
|
||||
if (chunk_size & 1)
|
||||
dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding.
|
||||
return dst + ChunkDiskSize(chunk);
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a WebPData object.
|
||||
// Manipulation of a WebPData object.
|
||||
|
||||
void WebPDataClear(WebPData* const webp_data) {
|
||||
if (webp_data != NULL) {
|
||||
@ -226,6 +226,19 @@ void WebPDataClear(WebPData* const webp_data) {
|
||||
}
|
||||
}
|
||||
|
||||
int WebPDataCopy(const WebPData* const src, WebPData* const dst) {
|
||||
if (src == NULL || dst == NULL) return 0;
|
||||
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
if (src->bytes_ != NULL && src->size_ != 0) {
|
||||
dst->bytes_ = (uint8_t*)malloc(src->size_);
|
||||
if (dst->bytes_ == NULL) return 0;
|
||||
memcpy((void*)dst->bytes_, src->bytes_, src->size_);
|
||||
dst->size_ = src->size_;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a MuxImage object.
|
||||
|
||||
@ -383,7 +396,7 @@ WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
|
||||
// MuxImage serialization methods.
|
||||
|
||||
// Size of an image.
|
||||
static size_t MuxImageDiskSize(const WebPMuxImage* wpi) {
|
||||
size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
|
||||
size_t size = 0;
|
||||
if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
|
||||
if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
|
||||
@ -400,7 +413,7 @@ size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list) {
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
|
||||
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
|
||||
// Ordering of chunks to be emitted is strictly as follows:
|
||||
// 1. Frame/Tile chunk (if present).
|
||||
// 2. Alpha chunk (if present).
|
||||
@ -420,6 +433,9 @@ uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
|
||||
int MuxHasLosslessImages(const WebPMuxImage* images) {
|
||||
while (images != NULL) {
|
||||
assert(images->img_ != NULL);
|
||||
@ -431,8 +447,13 @@ int MuxHasLosslessImages(const WebPMuxImage* images) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) {
|
||||
PutLE32(data + 0, mktag('R', 'I', 'F', 'F'));
|
||||
PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
|
||||
assert(size == (uint32_t)size);
|
||||
PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, mktag('W', 'E', 'B', 'P'));
|
||||
return data + RIFF_HEADER_SIZE;
|
||||
}
|
||||
|
||||
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
|
||||
assert(mux != NULL);
|
||||
|
@ -26,8 +26,7 @@ extern "C" {
|
||||
const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
|
||||
kChunks[(INDEX)].tag); \
|
||||
if (chunk) { \
|
||||
data->bytes_ = chunk->data_; \
|
||||
data->size_ = chunk->payload_size_; \
|
||||
*data = chunk->data_; \
|
||||
return WEBP_MUX_OK; \
|
||||
} else { \
|
||||
return WEBP_MUX_NOT_FOUND; \
|
||||
@ -199,7 +198,9 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) {
|
||||
err = MuxGet(mux, IDX_VP8X, 1, &data);
|
||||
if (err == WEBP_MUX_NOT_FOUND) {
|
||||
// Check if VP8/VP8L chunk is present.
|
||||
return WebPMuxGetImage(mux, &data, NULL);
|
||||
err = WebPMuxGetImage(mux, &data);
|
||||
WebPDataClear(&data);
|
||||
return err;
|
||||
} else if (err != WEBP_MUX_OK) {
|
||||
return err;
|
||||
}
|
||||
@ -211,12 +212,64 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) {
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
static uint8_t* EmitVP8XChunk(uint8_t* const dst, uint32_t width,
|
||||
uint32_t height, uint32_t flags) {
|
||||
const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
|
||||
PutLE32(dst, mktag('V', 'P', '8', 'X'));
|
||||
PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE);
|
||||
PutLE32(dst + CHUNK_HEADER_SIZE, flags);
|
||||
PutLE32(dst + CHUNK_HEADER_SIZE + 4, width);
|
||||
PutLE32(dst + CHUNK_HEADER_SIZE + 8, height);
|
||||
return dst + vp8x_size;
|
||||
}
|
||||
|
||||
// Assemble a single image WebP bitstream from 'wpi'.
|
||||
static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi,
|
||||
WebPData* const bitstream) {
|
||||
uint8_t* dst;
|
||||
|
||||
// Allocate data.
|
||||
const int need_vp8x = (wpi->alpha_ != NULL);
|
||||
const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0;
|
||||
const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0;
|
||||
// Note: No need to output FRM/TILE chunk for a single image.
|
||||
const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size +
|
||||
ChunkDiskSize(wpi->img_);
|
||||
uint8_t* const data = (uint8_t*)malloc(size);
|
||||
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
|
||||
// Main RIFF header.
|
||||
dst = MuxEmitRiffHeader(data, size);
|
||||
|
||||
if (need_vp8x) {
|
||||
int w, h;
|
||||
WebPMuxError err;
|
||||
assert(wpi->img_ != NULL);
|
||||
err = MuxGetImageWidthHeight(wpi->img_, &w, &h);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
free(data);
|
||||
return err;
|
||||
}
|
||||
dst = EmitVP8XChunk(dst, w, h, ALPHA_FLAG); // VP8X.
|
||||
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
|
||||
}
|
||||
|
||||
// Bitstream.
|
||||
dst = ChunkListEmit(wpi->img_, dst);
|
||||
assert(dst == data + size);
|
||||
|
||||
// Output.
|
||||
bitstream->bytes_ = data;
|
||||
bitstream->size_ = size;
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetImage(const WebPMux* const mux,
|
||||
WebPData* const image, WebPData* const alpha) {
|
||||
WebPData* const bitstream) {
|
||||
WebPMuxError err;
|
||||
WebPMuxImage* wpi = NULL;
|
||||
|
||||
if (mux == NULL || (image == NULL && alpha == NULL)) {
|
||||
if (mux == NULL || bitstream == NULL) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -228,24 +281,7 @@ WebPMuxError WebPMuxGetImage(const WebPMux* const mux,
|
||||
&wpi);
|
||||
assert(err == WEBP_MUX_OK); // Already tested above.
|
||||
|
||||
// Get alpha chunk (if present & requested).
|
||||
if (alpha != NULL) {
|
||||
memset(alpha, 0, sizeof(*alpha));
|
||||
if (wpi->alpha_ != NULL) {
|
||||
alpha->bytes_ = wpi->alpha_->data_;
|
||||
alpha->size_ = wpi->alpha_->payload_size_;
|
||||
}
|
||||
}
|
||||
|
||||
// Get image chunk.
|
||||
if (image != NULL) {
|
||||
memset(image, 0, sizeof(*image));
|
||||
if (wpi->img_ != NULL) {
|
||||
image->bytes_ = wpi->img_->data_;
|
||||
image->size_ = wpi->img_->payload_size_;
|
||||
}
|
||||
}
|
||||
return WEBP_MUX_OK;
|
||||
return SynthesizeBitstream(wpi, bitstream);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetMetadata(const WebPMux* const mux,
|
||||
@ -276,11 +312,9 @@ WebPMuxError WebPMuxGetLoopCount(const WebPMux* const mux,
|
||||
}
|
||||
|
||||
static WebPMuxError MuxGetFrameTileInternal(
|
||||
const WebPMux* const mux, uint32_t nth,
|
||||
WebPData* const image, WebPData* const alpha,
|
||||
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream,
|
||||
uint32_t* x_offset, uint32_t* y_offset, uint32_t* duration, uint32_t tag) {
|
||||
const uint8_t* frame_tile_data;
|
||||
size_t frame_tile_size;
|
||||
const WebPData* frame_tile_data;
|
||||
WebPMuxError err;
|
||||
WebPMuxImage* wpi;
|
||||
|
||||
@ -288,7 +322,7 @@ static WebPMuxError MuxGetFrameTileInternal(
|
||||
const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE;
|
||||
const WebPChunkId id = kChunks[idx].id;
|
||||
|
||||
if (mux == NULL || image == NULL ||
|
||||
if (mux == NULL || bitstream == NULL ||
|
||||
x_offset == NULL || y_offset == NULL || (is_frame && duration == NULL)) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
@ -298,47 +332,30 @@ static WebPMuxError MuxGetFrameTileInternal(
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// Get frame chunk.
|
||||
assert(wpi->header_ != NULL); // As GetNthImage() already checked header_.
|
||||
frame_tile_data = wpi->header_->data_;
|
||||
frame_tile_size = wpi->header_->payload_size_;
|
||||
assert(wpi->header_ != NULL); // As MuxImageGetNth() already checked header_.
|
||||
frame_tile_data = &wpi->header_->data_;
|
||||
|
||||
if (frame_tile_size < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
|
||||
*x_offset = GetLE32(frame_tile_data + 0);
|
||||
*y_offset = GetLE32(frame_tile_data + 4);
|
||||
if (is_frame) *duration = GetLE32(frame_tile_data + 16);
|
||||
if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
|
||||
*x_offset = GetLE32(frame_tile_data->bytes_ + 0);
|
||||
*y_offset = GetLE32(frame_tile_data->bytes_ + 4);
|
||||
if (is_frame) *duration = GetLE32(frame_tile_data->bytes_ + 16);
|
||||
|
||||
// Get alpha chunk (if present & requested).
|
||||
if (alpha != NULL) {
|
||||
memset(alpha, 0, sizeof(*alpha));
|
||||
if (wpi->alpha_ != NULL) {
|
||||
alpha->bytes_ = wpi->alpha_->data_;
|
||||
alpha->size_ = wpi->alpha_->payload_size_;
|
||||
}
|
||||
}
|
||||
|
||||
// Get image chunk.
|
||||
memset(image, 0, sizeof(*image));
|
||||
if (wpi->img_ != NULL) {
|
||||
image->bytes_ = wpi->img_->data_;
|
||||
image->size_ = wpi->img_->payload_size_;
|
||||
}
|
||||
|
||||
return WEBP_MUX_OK;
|
||||
return SynthesizeBitstream(wpi, bitstream);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetFrame(const WebPMux* const mux, uint32_t nth,
|
||||
WebPData* const image, WebPData* const alpha,
|
||||
WebPData* const bitstream,
|
||||
uint32_t* x_offset, uint32_t* y_offset,
|
||||
uint32_t* duration) {
|
||||
return MuxGetFrameTileInternal(mux, nth, image, alpha,
|
||||
return MuxGetFrameTileInternal(mux, nth, bitstream,
|
||||
x_offset, y_offset, duration,
|
||||
kChunks[IDX_FRAME].tag);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetTile(const WebPMux* const mux, uint32_t nth,
|
||||
WebPData* const image, WebPData* const alpha,
|
||||
WebPData* const bitstream,
|
||||
uint32_t* x_offset, uint32_t* y_offset) {
|
||||
return MuxGetFrameTileInternal(mux, nth, image, alpha,
|
||||
return MuxGetFrameTileInternal(mux, nth, bitstream,
|
||||
x_offset, y_offset, NULL,
|
||||
kChunks[IDX_TILE].tag);
|
||||
}
|
||||
|
Reference in New Issue
Block a user