diff --git a/README b/README index 64e9f2d6..4e294cbf 100644 --- a/README +++ b/README @@ -262,19 +262,20 @@ Use following options to convert into alternate image formats: -yuv ......... save the raw YUV samples in flat layout Other options are: - -version .... print version number and exit. - -nofancy ..... don't use the fancy YUV420 upscaler. - -nofilter .... disable in-loop filtering. - -nodither .... disable dithering. + -version .... print version number and exit + -nofancy ..... don't use the fancy YUV420 upscaler + -nofilter .... disable in-loop filtering + -nodither .... disable dithering -dither .. dithering strength (in 0..100) -mt .......... use multi-threading -crop ... crop output with the given rectangle -scale .......... scale the output (*after* any cropping) - -alpha ....... only save the alpha plane. + -flip ........ flip the output vertically + -alpha ....... only save the alpha plane -incremental . use incremental decoding (useful for tests) - -h ....... this help message. + -h ....... this help message -v ....... verbose (e.g. print encoding/decoding times) - -noasm ....... disable all assembly optimizations. + -noasm ....... disable all assembly optimizations Visualization tool: =================== diff --git a/examples/dwebp.c b/examples/dwebp.c index c1470466..cd5e1314 100644 --- a/examples/dwebp.c +++ b/examples/dwebp.c @@ -552,20 +552,21 @@ static void Help(void) { " -yuv ......... save the raw YUV samples in flat layout\n" "\n" " Other options are:\n" - " -version .... print version number and exit.\n" - " -nofancy ..... don't use the fancy YUV420 upscaler.\n" - " -nofilter .... disable in-loop filtering.\n" - " -nodither .... disable dithering.\n" + " -version .... print version number and exit\n" + " -nofancy ..... don't use the fancy YUV420 upscaler\n" + " -nofilter .... disable in-loop filtering\n" + " -nodither .... disable dithering\n" " -dither .. dithering strength (in 0..100)\n" " -mt .......... use multi-threading\n" " -crop ... crop output with the given rectangle\n" " -scale .......... scale the output (*after* any cropping)\n" - " -alpha ....... only save the alpha plane.\n" + " -flip ........ flip the output vertically\n" + " -alpha ....... only save the alpha plane\n" " -incremental . use incremental decoding (useful for tests)\n" - " -h ....... this help message.\n" + " -h ....... this help message\n" " -v ....... verbose (e.g. print encoding/decoding times)\n" #ifndef WEBP_DLL - " -noasm ....... disable all assembly optimizations.\n" + " -noasm ....... disable all assembly optimizations\n" #endif ); } @@ -641,6 +642,8 @@ int main(int argc, const char *argv[]) { config.options.use_scaling = 1; config.options.scaled_width = strtol(argv[++c], NULL, 0); config.options.scaled_height = strtol(argv[++c], NULL, 0); + } else if (!strcmp(argv[c], "-flip")) { + config.options.flip = 1; } else if (!strcmp(argv[c], "-v")) { verbose = 1; #ifndef WEBP_DLL diff --git a/man/dwebp.1 b/man/dwebp.1 index 02afed00..23243872 100644 --- a/man/dwebp.1 +++ b/man/dwebp.1 @@ -1,5 +1,5 @@ .\" Hey, EMACS: -*- nroff -*- -.TH DWEBP 1 "December 12, 2013" +.TH DWEBP 1 "January 11, 2014" .SH NAME dwebp \- decompress a WebP file to an image file .SH SYNOPSIS @@ -76,6 +76,9 @@ The top-left corner will be snapped to even coordinates if needed. This option is meant to reduce the memory needed for cropping large images. Note: the cropping is applied \fIbefore\fP any scaling. .TP +.B \-flip +Flip decoded image vertically (can be useful for OpenGL textures for instance). +.TP .BI \-scale " width height Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This option is mostly intended to reducing the memory needed to decode large images, diff --git a/src/dec/buffer.c b/src/dec/buffer.c index 1e852efe..3b0194f6 100644 --- a/src/dec/buffer.c +++ b/src/dec/buffer.c @@ -42,29 +42,34 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) { ok = 0; } else if (!WebPIsRGBMode(mode)) { // YUV checks const WebPYUVABuffer* const buf = &buffer->u.YUVA; - const uint64_t y_size = (uint64_t)buf->y_stride * height; - const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2); - const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2); - const uint64_t a_size = (uint64_t)buf->a_stride * height; + const int y_stride = abs(buf->y_stride); + const int u_stride = abs(buf->u_stride); + const int v_stride = abs(buf->v_stride); + const int a_stride = abs(buf->a_stride); + const uint64_t y_size = (uint64_t)y_stride * height; + const uint64_t u_size = (uint64_t)u_stride * ((height + 1) / 2); + const uint64_t v_size = (uint64_t)v_stride * ((height + 1) / 2); + const uint64_t a_size = (uint64_t)a_stride * height; ok &= (y_size <= buf->y_size); ok &= (u_size <= buf->u_size); ok &= (v_size <= buf->v_size); - ok &= (buf->y_stride >= width); - ok &= (buf->u_stride >= (width + 1) / 2); - ok &= (buf->v_stride >= (width + 1) / 2); + ok &= (y_stride >= width); + ok &= (u_stride >= (width + 1) / 2); + ok &= (v_stride >= (width + 1) / 2); ok &= (buf->y != NULL); ok &= (buf->u != NULL); ok &= (buf->v != NULL); if (mode == MODE_YUVA) { - ok &= (buf->a_stride >= width); + ok &= (a_stride >= width); ok &= (a_size <= buf->a_size); ok &= (buf->a != NULL); } } else { // RGB checks const WebPRGBABuffer* const buf = &buffer->u.RGBA; - const uint64_t size = (uint64_t)buf->stride * height; + const int stride = abs(buf->stride); + const uint64_t size = (uint64_t)stride * height; ok &= (size <= buf->size); - ok &= (buf->stride >= width * kModeBpp[mode]); + ok &= (stride >= width * kModeBpp[mode]); ok &= (buf->rgba != NULL); } return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM; @@ -131,9 +136,35 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { return CheckDecBuffer(buffer); } +VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) { + if (buffer == NULL) { + return VP8_STATUS_INVALID_PARAM; + } + if (WebPIsRGBMode(buffer->colorspace)) { + WebPRGBABuffer* const buf = &buffer->u.RGBA; + buf->rgba += (buffer->height - 1) * buf->stride; + buf->stride = -buf->stride; + } else { + WebPYUVABuffer* const buf = &buffer->u.YUVA; + const int H = buffer->height; + buf->y += (H - 1) * buf->y_stride; + buf->y_stride = -buf->y_stride; + buf->u += ((H - 1) >> 1) * buf->u_stride; + buf->u_stride = -buf->u_stride; + buf->v += ((H - 1) >> 1) * buf->v_stride; + buf->v_stride = -buf->v_stride; + if (buf->a != NULL) { + buf->a += (H - 1) * buf->a_stride; + buf->a_stride = -buf->a_stride; + } + } + return VP8_STATUS_OK; +} + VP8StatusCode WebPAllocateDecBuffer(int w, int h, const WebPDecoderOptions* const options, WebPDecBuffer* const out) { + VP8StatusCode status; if (out == NULL || w <= 0 || h <= 0) { return VP8_STATUS_INVALID_PARAM; } @@ -160,8 +191,15 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h, out->width = w; out->height = h; - // Then, allocate buffer for real - return AllocateBuffer(out); + // Then, allocate buffer for real. + status = AllocateBuffer(out); + if (status != VP8_STATUS_OK) return status; + + // Use the stride trick if vertical flip is needed. + if (options != NULL && options->flip) { + status = WebPFlipBuffer(out); + } + return status; } //------------------------------------------------------------------------------ diff --git a/src/dec/idec.c b/src/dec/idec.c index 40d5ff6e..765a59b3 100644 --- a/src/dec/idec.c +++ b/src/dec/idec.c @@ -246,6 +246,19 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) { return 1; } +// To be called last. +static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { + const WebPDecoderOptions* const options = idec->params_.options; + WebPDecBuffer* const output = idec->params_.output; + + idec->state_ = STATE_DONE; + if (options != NULL && options->flip) { + return WebPFlipBuffer(output); + } else { + return VP8_STATUS_OK; + } +} + //------------------------------------------------------------------------------ // Macroblock-decoding contexts @@ -476,9 +489,7 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { return IDecError(idec, VP8_STATUS_USER_ABORT); } dec->ready_ = 0; - idec->state_ = STATE_DONE; - - return VP8_STATUS_OK; + return FinishDecoding(idec); } static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec, @@ -530,9 +541,7 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) { return ErrorStatusLossless(idec, dec->status_); } - idec->state_ = STATE_DONE; - - return VP8_STATUS_OK; + return FinishDecoding(idec); } // Main decoding loop diff --git a/src/dec/webp.c b/src/dec/webp.c index fda88bda..36c99dea 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -512,6 +512,10 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size, if (status != VP8_STATUS_OK) { WebPFreeDecBuffer(params->output); } + + if (params->options != NULL && params->options->flip) { + status = WebPFlipBuffer(params->output); + } return status; } diff --git a/src/dec/webpi.h b/src/dec/webpi.h index d915f5ef..aa8088d4 100644 --- a/src/dec/webpi.h +++ b/src/dec/webpi.h @@ -93,10 +93,15 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, // dimension / etc.). If *options is not NULL, also verify that the options' // parameters are valid and apply them to the width/height dimensions of the // output buffer. This takes cropping / scaling / rotation into account. +// Also incorporates the options->flip flag to flip the buffer parameters if +// needed. VP8StatusCode WebPAllocateDecBuffer(int width, int height, const WebPDecoderOptions* const options, WebPDecBuffer* const buffer); +// Flip buffer vertically by negating the various strides. +VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer); + // Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the // memory (still held by 'src'). void WebPCopyDecBuffer(const WebPDecBuffer* const src, @@ -105,8 +110,6 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src, // Copy and transfer ownership from src to dst (beware of parameter order!) void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst); - - //------------------------------------------------------------------------------ #ifdef __cplusplus diff --git a/src/webp/decode.h b/src/webp/decode.h index 0c3b62e2..606d6ba2 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -20,7 +20,7 @@ extern "C" { #endif -#define WEBP_DECODER_ABI_VERSION 0x0203 // MAJOR(8b) + MINOR(8b) +#define WEBP_DECODER_ABI_VERSION 0x0204 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -442,11 +442,12 @@ struct WebPDecoderOptions { int scaled_width, scaled_height; // final resolution int use_threads; // if true, use multi-threaded decoding int dithering_strength; // dithering strength (0=Off, 100=full) + int flip; // flip output vertically // Unused for now: int force_rotation; // forced rotation (to be applied _last_) int no_enhancement; // if true, discard enhancement layer - uint32_t pad[5]; // padding for later use + uint32_t pad[4]; // padding for later use }; // Main object storing the configuration for advanced decoding.