add WebPMuxSetCanvasSize() to the mux API

previously, the final canvas size was adjusted tightly from the
animation frames. Now, it can be specified separately (to be larger, in particular).

calling WebPMuxSetCanvasSize(mux, 0, 0) triggers the 'adjust tightly' behaviour.
This can be useful after calling WebPMuxCreate() if further image addition
is expected.

-> Fixed gif2webp accordingly.

also: made WebPMuxAssemble() more robust by systematically zero-ing WebPData.

Change-Id: Ib4f7eac372cf9dbf6e25cd686a77960e386a0b7f
This commit is contained in:
skal 2014-06-30 07:00:49 +02:00
parent 1f3e5f1e60
commit 4536e7c49c
5 changed files with 111 additions and 31 deletions

View File

@ -442,14 +442,21 @@ int main(int argc, const char *argv[]) {
gif->SWidth, gif->SHeight);
}
}
// Allocate current buffer
// Set definitive canvas size.
err = WebPMuxSetCanvasSize(mux, gif->SWidth, gif->SHeight);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "Invalid canvas size %d x %d\n",
gif->SWidth, gif->SHeight);
goto End;
}
// Allocate current buffer.
frame.width = gif->SWidth;
frame.height = gif->SHeight;
frame.use_argb = 1;
if (!WebPPictureAlloc(&frame)) goto End;
WebPUtilClearPic(&frame, NULL);
// Initialize cache
// Initialize cache.
cache = WebPFrameCacheNew(frame.width, frame.height,
kmin, kmax, allow_mixed);
if (cache == NULL) goto End;

View File

@ -20,8 +20,10 @@
// Life of a mux object.
static void MuxInit(WebPMux* const mux) {
if (mux == NULL) return;
assert(mux != NULL);
memset(mux, 0, sizeof(*mux));
mux->canvas_width_ = 0; // just to be explicit
mux->canvas_height_ = 0;
}
WebPMux* WebPNewInternal(int version) {
@ -29,8 +31,7 @@ WebPMux* WebPNewInternal(int version) {
return NULL;
} else {
WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux));
// If mux is NULL MuxInit is a noop.
MuxInit(mux);
if (mux != NULL) MuxInit(mux);
return mux;
}
}
@ -43,7 +44,7 @@ static void DeleteAllImages(WebPMuxImage** const wpi_list) {
}
static void MuxRelease(WebPMux* const mux) {
if (mux == NULL) return;
assert(mux != NULL);
DeleteAllImages(&mux->images_);
ChunkListDelete(&mux->vp8x_);
ChunkListDelete(&mux->iccp_);
@ -54,10 +55,11 @@ static void MuxRelease(WebPMux* const mux) {
}
void WebPMuxDelete(WebPMux* mux) {
// If mux is NULL MuxRelease is a noop.
if (mux != NULL) {
MuxRelease(mux);
WebPSafeFree(mux);
}
}
//------------------------------------------------------------------------------
// Helper method(s).
@ -360,6 +362,32 @@ WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1);
}
WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
int width, int height) {
WebPMuxError err;
if (mux == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (width < 0 || height < 0 ||
width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if ((width * height) == 0 && (width | height) != 0) {
// one of width / height is zero, but not both -> invalid!
return WEBP_MUX_INVALID_ARGUMENT;
}
// If we already assembled a VP8X chunk, invalidate it.
err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
mux->canvas_width_ = width;
mux->canvas_height_ = height;
return WEBP_MUX_OK;
}
//------------------------------------------------------------------------------
// Delete API(s).
@ -413,8 +441,9 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
return WEBP_MUX_OK;
}
static WebPMuxError GetImageCanvasWidthHeight(
const WebPMux* const mux, uint32_t flags,
// Returns the tightest dimension for the canvas considering the image list.
static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
uint32_t flags,
int* const width, int* const height) {
WebPMuxImage* wpi = NULL;
assert(mux != NULL);
@ -513,12 +542,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
flags |= ALPHA_FLAG; // Some images have an alpha channel.
}
if (flags == 0) {
// For Simple Image, VP8X chunk should not be added.
return WEBP_MUX_OK;
}
err = GetImageCanvasWidthHeight(mux, flags, &width, &height);
err = GetAdjustedCanvasSize(mux, flags, &width, &height);
if (err != WEBP_MUX_OK) return err;
if (width <= 0 || height <= 0) {
@ -528,6 +552,19 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
return WEBP_MUX_INVALID_ARGUMENT;
}
if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) {
if (width > mux->canvas_width_ || height > mux->canvas_height_) {
return WEBP_MUX_INVALID_ARGUMENT;
}
width = mux->canvas_width_;
height = mux->canvas_height_;
}
if (flags == 0) {
// For Simple Image, VP8X chunk should not be added.
return WEBP_MUX_OK;
}
if (MuxHasAlpha(images)) {
// This means some frames explicitly/implicitly contain alpha.
// Note: This 'flags' update must NOT be done for a lossless image
@ -603,7 +640,13 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
uint8_t* dst = NULL;
WebPMuxError err;
if (mux == NULL || assembled_data == NULL) {
if (assembled_data == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
// Clean up returned data, in case something goes wrong.
memset(assembled_data, 0, sizeof(*assembled_data));
if (mux == NULL) {
return WEBP_MUX_INVALID_ARGUMENT;
}
@ -649,4 +692,3 @@ WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
}
//------------------------------------------------------------------------------

View File

@ -66,6 +66,8 @@ struct WebPMux {
WebPChunk* vp8x_;
WebPChunk* unknown_;
int canvas_width_;
int canvas_height_;
};
// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.

View File

@ -264,6 +264,10 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
if (id == WEBP_CHUNK_VP8X) { // grab global specs
mux->canvas_width_ = GetLE24(data + 12) + 1;
mux->canvas_height_ = GetLE24(data + 15) + 1;
}
break;
}
data += data_size;
@ -320,15 +324,21 @@ static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
f = GetLE32(data.bytes + 0);
w = GetLE24(data.bytes + 4) + 1;
h = GetLE24(data.bytes + 7) + 1;
} else { // Single image case.
} else {
const WebPMuxImage* const wpi = mux->images_;
WebPMuxError err = ValidateForSingleImage(mux);
if (err != WEBP_MUX_OK) return err;
// Grab user-forced canvas size as default.
w = mux->canvas_width_;
h = mux->canvas_height_;
if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
// single image and not forced canvas size => use dimension of first frame
assert(wpi != NULL);
w = wpi->width_;
h = wpi->height_;
}
if (wpi != NULL) {
if (wpi->has_alpha_) f |= ALPHA_FLAG;
}
}
if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
if (width != NULL) *width = w;
@ -537,4 +547,3 @@ WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
}
//------------------------------------------------------------------------------

View File

@ -55,7 +55,7 @@
extern "C" {
#endif
#define WEBP_MUX_ABI_VERSION 0x0101 // MAJOR(8b) + MINOR(8b)
#define WEBP_MUX_ABI_VERSION 0x0102 // MAJOR(8b) + MINOR(8b)
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
// the types are left here for reference.
@ -105,6 +105,7 @@ WEBP_EXTERN(WebPMux*) WebPNewInternal(int);
// Creates an empty mux object.
// Returns:
// A pointer to the newly created empty mux object.
// Or NULL in case of memory error.
static WEBP_INLINE WebPMux* WebPMuxNew(void) {
return WebPNewInternal(WEBP_MUX_ABI_VERSION);
}
@ -309,6 +310,24 @@ WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
//------------------------------------------------------------------------------
// Misc Utilities.
// Sets the canvas size for the mux object. The width and height can be
// specified explicitly or left as zero (0, 0).
// * When width and height are specified explicitly, then this frame bound is
// enforced during subsequent calls to WebPMuxAssemble() and an error is
// reported if any animated frame does not completely fit within the canvas.
// * When unspecified (0, 0), the constructed canvas will get the frame bounds
// from the bounding-box over all frames after calling WebPMuxAssemble().
// Parameters:
// mux - (in) object to which the canvas size is to be set
// width - (in) canvas width
// height - (in) canvas height
// Returns:
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
// width or height are invalid or out of bounds
// WEBP_MUX_OK - on success.
WEBP_EXTERN(WebPMuxError) WebPMuxSetCanvasSize(WebPMux* mux,
int width, int height);
// Gets the canvas size from the mux object.
// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
// That is, the mux object hasn't been modified since the last call to
@ -356,7 +375,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
// Note: The content of 'assembled_data' will be ignored and overwritten.
// Also, the content of 'assembled_data' is allocated using malloc(), and NOT
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
// WebPDataClear().
// WebPDataClear(). It's always safe to call WebPDataClear() upon return,
// even in case of error.
// Parameters:
// mux - (in/out) object whose chunks are to be assembled
// assembled_data - (out) assembled WebP data