From 227110c4c3c34fb1a0b1c11667d58e056a509815 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Wed, 28 Mar 2012 11:07:42 +0000 Subject: [PATCH] libwebp interface changes for lossless encoding. Change-Id: I703a1a18347acf78378cb23fddc6e5ca6dc6a0bb --- src/enc/config.c | 3 + src/enc/picture.c | 321 +++++++++++++++++++++++------------------ src/enc/vp8l.c | 230 +++++++++++++++++++++++++++++ src/enc/vp8li.h | 69 +++++++++ src/enc/webpenc.c | 47 +++--- src/utils/bit_writer.c | 79 ++++++++++ src/utils/bit_writer.h | 50 +++++++ src/webp/encode.h | 7 +- 8 files changed, 648 insertions(+), 158 deletions(-) create mode 100644 src/enc/vp8l.c create mode 100644 src/enc/vp8li.h diff --git a/src/enc/config.c b/src/enc/config.c index d51d9995..5e1b2540 100644 --- a/src/enc/config.c +++ b/src/enc/config.c @@ -44,6 +44,7 @@ int WebPConfigInitInternal(WebPConfig* const config, config->alpha_compression = 1; config->alpha_filtering = 1; config->alpha_quality = 100; + config->lossless = 0; // TODO(skal): tune. switch (preset) { @@ -116,6 +117,8 @@ int WebPValidateConfig(const WebPConfig* const config) { return 0; if (config->alpha_quality < 0 || config->alpha_quality > 100) return 0; + if (config->lossless < 0 || config->lossless > 1) + return 0; return 1; } diff --git a/src/enc/picture.c b/src/enc/picture.c index ef20608d..9885d2be 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -32,75 +32,88 @@ int WebPPictureAlloc(WebPPicture* const picture) { const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; const int width = picture->width; const int height = picture->height; - const int y_stride = width; - const int uv_width = HALVE(width); - const int uv_height = HALVE(height); - const int uv_stride = uv_width; - int uv0_stride = 0; - int a_width, a_stride; - uint64_t y_size, uv_size, uv0_size, a_size, total_size; - uint8_t* mem; - // U/V - switch (uv_csp) { - case WEBP_YUV420: - break; + if (!picture->use_argb_input) { + const int y_stride = width; + const int uv_width = HALVE(width); + const int uv_height = HALVE(height); + const int uv_stride = uv_width; + int uv0_stride = 0; + int a_width, a_stride; + uint64_t y_size, uv_size, uv0_size, a_size, total_size; + uint8_t* mem; + + // U/V + switch (uv_csp) { + case WEBP_YUV420: + break; #ifdef WEBP_EXPERIMENTAL_FEATURES - case WEBP_YUV400: // for now, we'll just reset the U/V samples - break; - case WEBP_YUV422: - uv0_stride = uv_width; - break; - case WEBP_YUV444: - uv0_stride = width; - break; + case WEBP_YUV400: // for now, we'll just reset the U/V samples + break; + case WEBP_YUV422: + uv0_stride = uv_width; + break; + case WEBP_YUV444: + uv0_stride = width; + break; #endif - default: + default: + return 0; + } + uv0_size = height * uv0_stride; + + // alpha + a_width = has_alpha ? width : 0; + a_stride = a_width; + y_size = (uint64_t)y_stride * height; + uv_size = (uint64_t)uv_stride * uv_height; + a_size = (uint64_t)a_stride * height; + + total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; + + // Security and validation checks + if (width <= 0 || height <= 0 || // check for luma/alpha param error + uv_width < 0 || uv_height < 0 || // check for u/v param error + y_size >= (1ULL << 40) || // check for reasonable global size + (size_t)total_size != total_size) { // check for overflow on 32bit return 0; - } - uv0_size = height * uv0_stride; + } + picture->y_stride = y_stride; + picture->uv_stride = uv_stride; + picture->a_stride = a_stride; + picture->uv0_stride = uv0_stride; + WebPPictureFree(picture); // erase previous buffer + mem = (uint8_t*)malloc((size_t)total_size); + if (mem == NULL) return 0; - // alpha - a_width = has_alpha ? width : 0; - a_stride = a_width; - y_size = (uint64_t)y_stride * height; - uv_size = (uint64_t)uv_stride * uv_height; - a_size = (uint64_t)a_stride * height; + picture->y = mem; + mem += y_size; - total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; + picture->u = mem; + mem += uv_size; + picture->v = mem; + mem += uv_size; - // Security and validation checks - if (width <= 0 || height <= 0 || // check for luma/alpha param error - uv_width < 0 || uv_height < 0 || // check for u/v param error - y_size >= (1ULL << 40) || // check for reasonable global size - (size_t)total_size != total_size) { // check for overflow on 32bit - return 0; - } - picture->y_stride = y_stride; - picture->uv_stride = uv_stride; - picture->a_stride = a_stride; - picture->uv0_stride = uv0_stride; - WebPPictureFree(picture); // erase previous buffer - mem = (uint8_t*)malloc((size_t)total_size); - if (mem == NULL) return 0; - - picture->y = mem; - mem += y_size; - - picture->u = mem; - mem += uv_size; - picture->v = mem; - mem += uv_size; - - if (a_size) { - picture->a = mem; - mem += a_size; - } - if (uv0_size) { - picture->u0 = mem; - mem += uv0_size; - picture->v0 = mem; - mem += uv0_size; + if (a_size) { + picture->a = mem; + mem += a_size; + } + if (uv0_size) { + picture->u0 = mem; + mem += uv0_size; + picture->v0 = mem; + mem += uv0_size; + } + } else { + if (width <= 0 || height <= 0 || + width >= 0x4000 || height >= 0x4000) { + return 0; + } + WebPPictureFree(picture); // erase previous buffer + picture->argb = (uint32_t*)malloc(width * height * + sizeof(*picture->argb)); + if (picture->argb == NULL) return 0; + picture->argb_stride = width; } } return 1; @@ -114,12 +127,14 @@ static void WebPPictureGrabSpecs(const WebPPicture* const src, dst->y = dst->u = dst->v = NULL; dst->u0 = dst->v0 = NULL; dst->a = NULL; + dst->argb = NULL; } // Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* const picture) { if (picture != NULL) { free(picture->y); + free(picture->argb); WebPPictureGrabSpecs(NULL, picture); } } @@ -144,28 +159,34 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { WebPPictureGrabSpecs(src, dst); if (!WebPPictureAlloc(dst)) return 0; - CopyPlane(src->y, src->y_stride, - dst->y, dst->y_stride, dst->width, dst->height); - CopyPlane(src->u, src->uv_stride, - dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - CopyPlane(src->v, src->uv_stride, - dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - if (dst->a != NULL) { - CopyPlane(src->a, src->a_stride, - dst->a, dst->a_stride, dst->width, dst->height); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (dst->u0 != NULL) { - int uv0_width = src->width; - if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { - uv0_width = HALVE(uv0_width); + if (!src->use_argb_input) { + CopyPlane(src->y, src->y_stride, + dst->y, dst->y_stride, dst->width, dst->height); + CopyPlane(src->u, src->uv_stride, + dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); + CopyPlane(src->v, src->uv_stride, + dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); + if (dst->a != NULL) { + CopyPlane(src->a, src->a_stride, + dst->a, dst->a_stride, dst->width, dst->height); + } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (dst->u0 != NULL) { + int uv0_width = src->width; + if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { + uv0_width = HALVE(uv0_width); + } + CopyPlane(src->u0, src->uv0_stride, + dst->u0, dst->uv0_stride, uv0_width, dst->height); + CopyPlane(src->v0, src->uv0_stride, + dst->v0, dst->uv0_stride, uv0_width, dst->height); } - CopyPlane(src->u0, src->uv0_stride, - dst->u0, dst->uv0_stride, uv0_width, dst->height); - CopyPlane(src->v0, src->uv0_stride, - dst->v0, dst->uv0_stride, uv0_width, dst->height); - } #endif + } else { + CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, + (uint8_t*)dst->argb, 4 * dst->argb_stride, + 4 * dst->width, dst->height); + } return 1; } @@ -438,64 +459,88 @@ static int Import(WebPPicture* const picture, const int width = picture->width; const int height = picture->height; - // Import luma plane - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int offset = step * x + y * rgb_stride; - picture->y[x + y * picture->y_stride] = - rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); - } - } - - // Downsample U/V plane - if (uv_csp != WEBP_YUV400) { - for (y = 0; y < (height >> 1); ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM4); - } - if (picture->width & 1) { - RGB_TO_UV(x, y, SUM2V); - } - } - if (height & 1) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV(x, y, SUM1); - } - } - -#ifdef WEBP_EXPERIMENTAL_FEATURES - // Store original U/V samples too - if (uv_csp == WEBP_YUV422) { - for (y = 0; y < height; ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV0(2 * x, x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV0(2 * x, x, y, SUM1); - } - } - } else if (uv_csp == WEBP_YUV444) { - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - RGB_TO_UV0(x, x, y, SUM1); - } - } - } -#endif - } else { - MakeGray(picture); - } - - if (import_alpha) { - const uint8_t* const a_ptr = rgb + 3; - assert(step >= 4); + if (!picture->use_argb_input) { + // Import luma plane for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - picture->a[x + y * picture->a_stride] = - a_ptr[step * x + y * rgb_stride]; + const int offset = step * x + y * rgb_stride; + picture->y[x + y * picture->y_stride] = + rgb_to_y(r_ptr[offset], g_ptr[offset], b_ptr[offset]); + } + } + + // Downsample U/V plane + if (uv_csp != WEBP_YUV400) { + for (y = 0; y < (height >> 1); ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM4); + } + if (picture->width & 1) { + RGB_TO_UV(x, y, SUM2V); + } + } + if (height & 1) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV(x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV(x, y, SUM1); + } + } + +#ifdef WEBP_EXPERIMENTAL_FEATURES + // Store original U/V samples too + if (uv_csp == WEBP_YUV422) { + for (y = 0; y < height; ++y) { + for (x = 0; x < (width >> 1); ++x) { + RGB_TO_UV0(2 * x, x, y, SUM2H); + } + if (width & 1) { + RGB_TO_UV0(2 * x, x, y, SUM1); + } + } + } else if (uv_csp == WEBP_YUV444) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + RGB_TO_UV0(x, x, y, SUM1); + } + } + } +#endif + } else { + MakeGray(picture); + } + + if (import_alpha) { + const uint8_t* const a_ptr = rgb + 3; + assert(step >= 4); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + picture->a[x + y * picture->a_stride] = + a_ptr[step * x + y * rgb_stride]; + } + } + } + } else { + if (!import_alpha) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int offset = step * x + y * rgb_stride; + const uint32_t argb = 0xff000000 | (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | (b_ptr[offset]); + picture->argb[x + y * picture->argb_stride] = argb; + } + } + } else { + const uint8_t* const a_ptr = rgb + 3; + assert(step >= 4); + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const int offset = step * x + y * rgb_stride; + const uint32_t argb = (a_ptr[offset] << 24) | (r_ptr[offset] << 16) | + (g_ptr[offset] << 8) | (b_ptr[offset]); + picture->argb[x + y * picture->argb_stride] = argb; + } } } } diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c new file mode 100644 index 00000000..228ee446 --- /dev/null +++ b/src/enc/vp8l.c @@ -0,0 +1,230 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// This code is licensed under the same terms as WebM: +// Software License Agreement: http://www.webmproject.org/license/software/ +// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ +// ----------------------------------------------------------------------------- +// +// main entry for the decoder +// +// Author: Vikas Arora (vikaas.arora@gmail.com) +// + +#include +#include +#include +#include "./vp8enci.h" +#include "./vp8li.h" +#include "../utils/bit_writer.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +static const uint32_t kImageSizeBits = 14; + +static int VP8LEncAnalyze(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int EncodeImageInternal(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int CreatePalette(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static void EvalSubtractGreen(VP8LEncoder* const enc) { + (void)enc; +} + +static int ApplyPredictFilter(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static int ApplyCrossColorFilter(VP8LEncoder* const enc) { + (void)enc; + return 1; +} + +static void EvalEmergingPalette(VP8LEncoder* const enc) { + (void)enc; +} + +static void PutLE32(uint8_t* const data, uint32_t val) { + data[0] = (val >> 0) & 0xff; + data[1] = (val >> 8) & 0xff; + data[2] = (val >> 16) & 0xff; + data[3] = (val >> 24) & 0xff; +} + +static WebPEncodingError WriteRiffHeader(VP8LEncoder* const enc, + size_t riff_size, size_t vp8l_size) { + const WebPPicture* const pic = enc->pic_; + uint8_t riff[HEADER_SIZE + SIGNATURE_SIZE] = { + 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', + 'V', 'P', '8', 'L', 0, 0, 0, 0, LOSSLESS_MAGIC_BYTE, + }; + if (riff_size < (vp8l_size + TAG_SIZE + CHUNK_HEADER_SIZE)) { + return VP8_ENC_ERROR_INVALID_CONFIGURATION; + } + PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); + PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size); + if (!pic->writer(riff, sizeof(riff), pic)) { + return VP8_ENC_ERROR_BAD_WRITE; + } + return VP8_ENC_OK; +} + +static WebPEncodingError WriteImage(VP8LEncoder* const enc) { + size_t riff_size, vp8l_size, webpll_size, pad; + WebPPicture* const pic = enc->pic_; + WebPEncodingError err = VP8_ENC_OK; + const uint8_t* const webpll_data = VP8LBitWriterFinish(&enc->bw_); + + webpll_size = VP8LBitWriterNumBytes(&enc->bw_); + vp8l_size = SIGNATURE_SIZE + webpll_size; + pad = vp8l_size & 1; + vp8l_size += pad; + + riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size; + err = WriteRiffHeader(enc, riff_size, vp8l_size); + if (err != VP8_ENC_OK) goto Error; + + if (!pic->writer(webpll_data, webpll_size, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + + if (pad) { + const uint8_t pad_byte[1] = { 0 }; + if (!pic->writer(pad_byte, 1, pic)) { + err = VP8_ENC_ERROR_BAD_WRITE; + goto Error; + } + } + return VP8_ENC_OK; + + Error: + WebPEncodingSetError(pic, err); + return err; +} + +static VP8LEncoder* InitVP8LEncoder(const WebPConfig* const config, + WebPPicture* const picture) { + VP8LEncoder* enc; + const int kEstmatedEncodeSize = (picture->width * picture->height) >> 1; + (void)config; + + enc = (VP8LEncoder*)malloc(sizeof(*enc)); + if (enc == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } + enc->pic_ = picture; + enc->use_lz77_ = 1; + enc->palette_bits_ = 7; + + // TODO: Use config.quality to initialize histo_bits_ and transform_bits_. + enc->histo_bits_ = 4; + enc->transform_bits_ = 4; + VP8LBitWriterInit(&enc->bw_, kEstmatedEncodeSize); + + return enc; +} + +static WebPEncodingError WriteImageSize(VP8LEncoder* const enc) { + WebPEncodingError status = VP8_ENC_OK; + WebPPicture* const pic = enc->pic_; + const int width = pic->width - 1; + const int height = pic->height - 1; + if (width < 0x4000 && height < 0x4000) { + VP8LWriteBits(&enc->bw_, kImageSizeBits, width); + VP8LWriteBits(&enc->bw_, kImageSizeBits, height); + } else { + status = VP8_ENC_ERROR_BAD_DIMENSION; + } + return status; +} + +static void DeleteVP8LEncoder(VP8LEncoder* enc) { + free(enc); +} + +int VP8LEncodeImage(const WebPConfig* const config, + WebPPicture* const picture) { + int ok = 0; + VP8LEncoder* enc = NULL; + WebPEncodingError err = VP8_ENC_OK; + + if (config == NULL || picture == NULL) return 0; + if (picture->argb == NULL) return 0; + enc = InitVP8LEncoder(config, picture); + if (enc == NULL) goto Error; + + // --------------------------------------------------------------------------- + // Analyze image (entropy, num_palettes etc) + + if (!VP8LEncAnalyze(enc)) goto Error; + + if (enc->use_palette_) { + CreatePalette(enc); + } + + // Write image size. + err = WriteImageSize(enc); + if (err != VP8_ENC_OK) { + ok = 0; + goto Error; + } + + // --------------------------------------------------------------------------- + // Apply transforms and write transform data. + + EvalSubtractGreen(enc); + + if (enc->use_predict_) { + if (!ApplyPredictFilter(enc)) goto Error; + } + + if (enc->use_cross_color_) { + if (!ApplyCrossColorFilter(enc)) goto Error; + } + + if (enc->use_emerging_palette_) { + EvalEmergingPalette(enc); + } + + // --------------------------------------------------------------------------- + // Encode and write the transformed image. + + ok = EncodeImageInternal(enc); + if (!ok) goto Error; + + err = WriteImage(enc); + if (err != VP8_ENC_OK) { + ok = 0; + goto Error; + } + + Error: + VP8LBitWriterDestroy(&enc->bw_); + DeleteVP8LEncoder(enc); + if (!ok) { + // TODO(vikasa): err is not set for all error paths. Set default err. + if (err == VP8_ENC_OK) err = VP8_ENC_ERROR_BAD_WRITE; + WebPEncodingSetError(picture, err); + } + return ok; +} + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/enc/vp8li.h b/src/enc/vp8li.h new file mode 100644 index 00000000..e46e2d2f --- /dev/null +++ b/src/enc/vp8li.h @@ -0,0 +1,69 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// This code is licensed under the same terms as WebM: +// Software License Agreement: http://www.webmproject.org/license/software/ +// Additional IP Rights Grant: http://www.webmproject.org/license/additional/ +// ----------------------------------------------------------------------------- +// +// Lossless encoder: internal header. +// +// Author: Vikas Arora(vikaas.arora@gmail.com) + +#ifndef WEBP_ENC_VP8LI_H_ +#define WEBP_ENC_VP8LI_H_ + +#include "../webp/encode.h" +#include "../utils/bit_writer.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// TODO(vikasa): factorize these with ones used in lossless decoder. +#define TAG_SIZE 4 +#define CHUNK_HEADER_SIZE 8 +#define RIFF_HEADER_SIZE 12 +#define HEADER_SIZE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) +#define SIGNATURE_SIZE 1 +#define LOSSLESS_MAGIC_BYTE 0x64 + +typedef struct { + const WebPConfig* config_; // user configuration and parameters + WebPPicture* pic_; // input / output picture + + // Encoding parameters derived from quality parameter. + int use_lz77_; + int palette_bits_; + int histo_bits_; + int transform_bits_; + + // Encoding parameters derived from image characteristics. + int predicted_bits_; + int non_predicted_bits_; + int use_palette_; + int num_palette_colors; + int use_predict_; + int use_cross_color_; + int use_emerging_palette_; + + VP8LBitWriter bw_; +} VP8LEncoder; + +//------------------------------------------------------------------------------ +// internal functions. Not public. + +// in vp8l.c + +// Encodes the picture. +// Returns 0 if config or picture is NULL or picture doesn't have valid argb +// input. +int VP8LEncodeImage(const WebPConfig* const config, + WebPPicture* const picture); + +//------------------------------------------------------------------------------ + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif + +#endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 714d9811..00a3d5cc 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -15,6 +15,7 @@ #include #include "./vp8enci.h" +#include "./vp8li.h" // #define PRINT_MEMORY_INFO @@ -142,8 +143,8 @@ static void MapConfigToTools(VP8Encoder* const enc) { // LFStats: 2048 // Picture size (yuv): 589824 -static VP8Encoder* InitEncoder(const WebPConfig* const config, - WebPPicture* const picture) { +static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, + WebPPicture* const picture) { const int use_filter = (config->filter_strength > 0) || (config->autofilter > 0); const int mb_w = (picture->width + 15) >> 4; @@ -259,7 +260,7 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config, return enc; } -static void DeleteEncoder(VP8Encoder* enc) { +static void DeleteVP8Encoder(VP8Encoder* enc) { if (enc) { VP8EncDeleteAlpha(enc); #ifdef WEBP_EXPERIMENTAL_FEATURES @@ -327,7 +328,6 @@ int WebPReportProgress(VP8Encoder* const enc, int percent) { //------------------------------------------------------------------------------ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { - VP8Encoder* enc; int ok; if (pic == NULL) @@ -339,27 +339,36 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); if (pic->width <= 0 || pic->height <= 0) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - if (pic->y == NULL || pic->u == NULL || pic->v == NULL) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - enc = InitEncoder(config, pic); - if (enc == NULL) return 0; // pic->error is already set. - // Note: each of the tasks below account for 20% in the progress report. - ok = VP8EncAnalyze(enc) - && VP8StatLoop(enc) - && VP8EncLoop(enc) - && VP8EncFinishAlpha(enc) + if (!config->lossless) { + VP8Encoder* enc = NULL; + if (pic->y == NULL || pic->u == NULL || pic->v == NULL) + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); + + enc = InitVP8Encoder(config, pic); + if (enc == NULL) return 0; // pic->error is already set. + // Note: each of the tasks below account for 20% in the progress report. + ok = VP8EncAnalyze(enc) + && VP8StatLoop(enc) + && VP8EncLoop(enc) + && VP8EncFinishAlpha(enc) #ifdef WEBP_EXPERIMENTAL_FEATURES - && VP8EncFinishLayer(enc) + && VP8EncFinishLayer(enc) #endif - && VP8EncWrite(enc); - StoreStats(enc); - if (!ok) { - VP8EncFreeBitWriters(enc); + && VP8EncWrite(enc); + StoreStats(enc); + if (!ok) { + VP8EncFreeBitWriters(enc); + } + DeleteVP8Encoder(enc); + } else { + if (pic->argb == NULL) + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); + + ok = VP8LEncodeImage(config, pic); } - DeleteEncoder(enc); return ok; } diff --git a/src/utils/bit_writer.c b/src/utils/bit_writer.c index 6ab44833..04c0db63 100644 --- a/src/utils/bit_writer.c +++ b/src/utils/bit_writer.c @@ -8,6 +8,7 @@ // Bit writing and boolean coder // // Author: Skal (pascal.massimino@gmail.com) +// Vikas Arora (vikaas.arora@gmail.com) #include #include // for memcpy() @@ -186,6 +187,84 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) { } } +//------------------------------------------------------------------------------ +// VP8LBitWriter + +// Returns 1 on success. +static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) { + uint8_t* allocated_buf; + size_t allocated_size; + const size_t size_required = VP8LBitWriterNumBytes(bw) + extra_size; + if ((bw->max_bytes_ > 0) && (size_required <= bw->max_bytes_)) return 1; + allocated_size = (3 * bw->max_bytes_) >> 1; + if (allocated_size < size_required) { + allocated_size = size_required; + } + // Make Allocated size multiple of KBs + allocated_size = (((allocated_size >> 10) + 1) << 10); + allocated_buf = (uint8_t*)malloc(allocated_size); + if (allocated_buf == NULL) return 0; + memset(allocated_buf, 0, allocated_size); + if (bw->bit_pos_ > 0) { + memcpy(allocated_buf, bw->buf_, VP8LBitWriterNumBytes(bw)); + } + free(bw->buf_); + bw->buf_ = allocated_buf; + bw->max_bytes_ = allocated_size; + return 1; +} + +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) { + memset(bw, 0, sizeof(*bw)); + return VP8LBitWriterResize(bw, expected_size); +} + +void VP8LBitWriterDestroy(VP8LBitWriter* const bw) { + if (bw != NULL) { + free(bw->buf_); + memset(bw, 0, sizeof(*bw)); + } +} + +void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) { + if (n_bits < 1) return; +#if !defined(__BIG_ENDIAN__) + // Technically, this branch of the code can write up to 25 bits at a time, + // but in deflate, the maximum number of bits written is 16 at a time. + { + uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3]; + uint32_t v = *(const uint32_t*)(p); + v |= bits << (bw->bit_pos_ & 7); + *(uint32_t*)(p) = v; + bw->bit_pos_ += n_bits; + } +#else // LITTLE_ENDIAN + // implicit & 0xff is assumed for uint8_t arithmetics + { + uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3]; + const int bits_reserved_in_first_byte = (bw->bit_pos_ & 7); + *p++ |= (bits << bits_reserved_in_first_byte); + const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte; + if (bits_left_to_write >= 1) { + *p++ = bits >> (8 - bits_reserved_in_first_byte); + if (bits_left_to_write >= 9) { + *p++ = bits >> (16 - bits_reserved_in_first_byte); + } + } + *p = 0; + bw->bit_pos_ += n_bits; + } +#endif // BIG_ENDIAN + if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) { + const size_t kAdditionalBuffer = 32768 + bw->max_bytes_; + if (!VP8LBitWriterResize(bw, kAdditionalBuffer)) { + bw->bit_pos_ = 0; + bw->error_ = 1; + } + } +} + + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/utils/bit_writer.h b/src/utils/bit_writer.h index c85356fe..40ea86fb 100644 --- a/src/utils/bit_writer.h +++ b/src/utils/bit_writer.h @@ -64,6 +64,56 @@ static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { return bw->pos_; } +//------------------------------------------------------------------------------ +// VP8LBitWriter +// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope +// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is +// implemented. + +typedef struct { + uint8_t* buf_; + size_t bit_pos_; + size_t max_bytes_; + + // After all bits are written, the caller must observe the state of + // error_. A value of 1 indicates that a memory allocation failure + // has happened during bit writing. A value of 0 indicates successful + // writing of bits. + int error_; +} VP8LBitWriter; + +static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) { + return (bw->bit_pos_ + 7) >> 3; +} + +static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { + return bw->buf_; +} + +// Returns 1 on success. +int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); + +void VP8LBitWriterDestroy(VP8LBitWriter* const bw); + +// This function writes bits into bytes in increasing addresses, and within +// a byte least-significant-bit first. +// +// The function can write up to 16 bits in one go with WriteBits +// Example: let's assume that 3 bits (Rs below) have been written already: +// +// BYTE-0 BYTE+1 BYTE+2 +// +// 0000 0RRR 0000 0000 0000 0000 +// +// Now, we could write 5 or less bits in MSB by just sifting by 3 +// and OR'ing to BYTE-0. +// +// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0, +// and locate the rest in BYTE+1 and BYTE+2. +// +// returns 1 on success. +void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits); + //------------------------------------------------------------------------------ #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/webp/encode.h b/src/webp/encode.h index cabc9823..8704810f 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -76,6 +76,7 @@ typedef struct { // 0: none, 1: fast, 2: best. Default if 1. int alpha_quality; // Between 0 (smallest size) and 100 (lossless). // Default is 100. + int lossless; // Lossless encoding (0=lossy(default), 1=lossless). } WebPConfig; // Enumerate some predefined settings for WebPConfig, depending on the type @@ -189,7 +190,7 @@ struct WebPPicture { int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION) uint8_t *y, *u, *v; // pointers to luma/chroma planes. int y_stride, uv_stride; // luma/chroma strides. - uint8_t *a; // pointer to the alpha plane + uint8_t* a; // pointer to the alpha plane int a_stride; // stride of the alpha plane // output @@ -216,6 +217,10 @@ struct WebPPicture { WebPEncodingError error_code; // error code in case of problem. WebPProgressHook progress_hook; // if not NULL, called while encoding. + + int use_argb_input; // Flag for encoder to read argb pixels as input. + uint32_t* argb; // Pointer to argb (32 bit) plane. + int argb_stride; // This is stride in pixels units, not bytes. }; // Internal, version-checked, entry point