From 95509f9914afbeb91c4f20414dcdeb0cf88161b1 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Wed, 14 Oct 2015 00:25:42 +0200 Subject: [PATCH] large re-organization of the delta-palettization code same functionality, but better code layout. What changed: * don't trash the palette_[] in EncodePalette(), so it can be re-used * split generation of image from bit-stream coding * move all the delta-palette code to delta_palettization.c, and only have 1 entry point there WebPSearchOptimalDeltaPalette() * minimize the number of "#ifdef WEBP_EXPERIMENTAL_FEATURES" in vp8l.c * clarify the TransformBuffer stuff. more clean-up to come here... This should make experimenting with delta-palettization easier and more compartimentalized. Change-Id: Iadaa90e6c5b9dabc7791aec2530e18c973a94610 --- src/dsp/lossless.c | 100 --------------- src/enc/delta_palettization.c | 208 ++++++++++++++++++++++++++++++- src/enc/delta_palettization.h | 27 ++-- src/enc/vp8l.c | 225 ++++++++++++++++------------------ 4 files changed, 313 insertions(+), 247 deletions(-) diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c index f634b8dc..5ca77594 100644 --- a/src/dsp/lossless.c +++ b/src/dsp/lossless.c @@ -634,103 +634,3 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInit(void) { } //------------------------------------------------------------------------------ - -#ifdef WEBP_EXPERIMENTAL_FEATURES -// Delta palettization functions. -static WEBP_INLINE int Square(int x) { - return x * x; -} - -static WEBP_INLINE uint32_t Intensity(uint32_t a) { - return - 30 * ((a >> 16) & 0xff) + - 59 * ((a >> 8) & 0xff) + - 11 * ((a >> 0) & 0xff); -} - -static uint32_t CalcDist(uint32_t predicted_value, uint32_t actual_value, - uint32_t palette_entry) { - int i; - uint32_t distance = 0; - AddPixelsEq(&predicted_value, palette_entry); - for (i = 0; i < 32; i += 8) { - const int32_t av = (actual_value >> i) & 0xff; - const int32_t pv = (predicted_value >> i) & 0xff; - distance += Square(pv - av); - } - // We sum square of intensity difference with factor 10, but because Intensity - // returns 100 times real intensity we need to multiply differences of colors - // by 1000. - distance *= 1000u; - distance += Square(Intensity(predicted_value) - - Intensity(actual_value)); - return distance; -} - -static uint32_t Predict(int x, int y, uint32_t* image) { - const uint32_t t = y == 0 ? ARGB_BLACK : image[x]; - const uint32_t l = x == 0 ? ARGB_BLACK : image[x - 1]; - const uint32_t p = - (((((t >> 24) & 0xff) + ((l >> 24) & 0xff)) / 2) << 24) + - (((((t >> 16) & 0xff) + ((l >> 16) & 0xff)) / 2) << 16) + - (((((t >> 8) & 0xff) + ((l >> 8) & 0xff)) / 2) << 8) + - (((((t >> 0) & 0xff) + ((l >> 0) & 0xff)) / 2) << 0); - if (x == 0 && y == 0) return ARGB_BLACK; - if (x == 0) return t; - if (y == 0) return l; - return p; -} - -static WEBP_INLINE int AddSubtractComponentFullWithCoefficient( - int a, int b, int c) { - return Clip255(a + ((b - c) >> 2)); -} - -static WEBP_INLINE uint32_t ClampedAddSubtractFullWithCoefficient( - uint32_t c0, uint32_t c1, uint32_t c2) { - const int a = AddSubtractComponentFullWithCoefficient( - c0 >> 24, c1 >> 24, c2 >> 24); - const int r = AddSubtractComponentFullWithCoefficient((c0 >> 16) & 0xff, - (c1 >> 16) & 0xff, - (c2 >> 16) & 0xff); - const int g = AddSubtractComponentFullWithCoefficient((c0 >> 8) & 0xff, - (c1 >> 8) & 0xff, - (c2 >> 8) & 0xff); - const int b = AddSubtractComponentFullWithCoefficient( - c0 & 0xff, c1 & 0xff, c2 & 0xff); - return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; -} - -int FindBestPaletteEntry(int x, int y, const uint32_t* palette, - int palette_size, uint32_t* src, - int src_stride, uint32_t* new_image) { - int i; - const uint32_t predicted_value = Predict(x, y, new_image); - int idx = 0; - uint32_t best_distance = CalcDist(predicted_value, src[x], palette[0]); - for (i = 1; i < palette_size; ++i) { - const uint32_t distance = CalcDist(predicted_value, src[x], palette[i]); - if (distance < best_distance) { - best_distance = distance; - idx = i; - } - } - - { - uint32_t new_value = predicted_value; - AddPixelsEq(&new_value, palette[idx]); - if (x > 0) { - src[x - 1] = ClampedAddSubtractFullWithCoefficient( - src[x - 1], new_value, src[x]); - } - if (y > 0) { - src[x - src_stride] = - ClampedAddSubtractFullWithCoefficient( - src[x - src_stride], new_value, src[x]); - } - new_image[x] = new_value; - } - return idx; -} - -#endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/enc/delta_palettization.c b/src/enc/delta_palettization.c index e8f8cc24..062e588d 100644 --- a/src/enc/delta_palettization.c +++ b/src/enc/delta_palettization.c @@ -10,16 +10,23 @@ // Author: Mislav Bradac (mislavm@google.com) // -#ifdef WEBP_EXPERIMENTAL_FEATURES - #include "./delta_palettization.h" + +#ifdef WEBP_EXPERIMENTAL_FEATURES #include "../webp/types.h" +#include "../dsp/lossless.h" #define MK_COL(r, g, b) (((r) << 16) + ((g) << 8) + (b)) +// Format allows palette up to 256 entries, but more palette entries produce +// bigger entropy. In the future it will probably be useful to add more entries +// that are far from the origin of the palette or choose remaining entries +// dynamically. +#define DELTA_PALETTE_SIZE 226 + // Palette used for delta_palettization. Entries are roughly sorted by distance // of their signed equivalents from the origin. -const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { +static const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { MK_COL(0u, 0u, 0u), MK_COL(255u, 255u, 255u), MK_COL(1u, 1u, 1u), @@ -249,9 +256,200 @@ const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE] = { #undef MK_COL +//------------------------------------------------------------------------------ +// TODO(skal): move the functions to dsp/lossless.c when the correct +// granularity is found. For now, we'll just copy-paste some useful bits +// here instead. + +// In-place sum of each component with mod 256. +static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { + const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); + const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); + *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + +static WEBP_INLINE uint32_t Clip255(uint32_t a) { + if (a < 256) { + return a; + } + // return 0, when a is a negative integer. + // return 255, when a is positive. + return ~a >> 24; +} + +// Delta palettization functions. +static WEBP_INLINE int Square(int x) { + return x * x; +} + +static WEBP_INLINE uint32_t Intensity(uint32_t a) { + return + 30 * ((a >> 16) & 0xff) + + 59 * ((a >> 8) & 0xff) + + 11 * ((a >> 0) & 0xff); +} + +static uint32_t CalcDist(uint32_t predicted_value, uint32_t actual_value, + uint32_t palette_entry) { + int i; + uint32_t distance = 0; + AddPixelsEq(&predicted_value, palette_entry); + for (i = 0; i < 32; i += 8) { + const int32_t av = (actual_value >> i) & 0xff; + const int32_t pv = (predicted_value >> i) & 0xff; + distance += Square(pv - av); + } + // We sum square of intensity difference with factor 10, but because Intensity + // returns 100 times real intensity we need to multiply differences of colors + // by 1000. + distance *= 1000u; + distance += Square(Intensity(predicted_value) + - Intensity(actual_value)); + return distance; +} + +static uint32_t Predict(int x, int y, uint32_t* image) { + const uint32_t t = (y == 0) ? ARGB_BLACK : image[x]; + const uint32_t l = (x == 0) ? ARGB_BLACK : image[x - 1]; + const uint32_t p = + (((((t >> 24) & 0xff) + ((l >> 24) & 0xff)) / 2) << 24) + + (((((t >> 16) & 0xff) + ((l >> 16) & 0xff)) / 2) << 16) + + (((((t >> 8) & 0xff) + ((l >> 8) & 0xff)) / 2) << 8) + + (((((t >> 0) & 0xff) + ((l >> 0) & 0xff)) / 2) << 0); + if (x == 0 && y == 0) return ARGB_BLACK; + if (x == 0) return t; + if (y == 0) return l; + return p; +} + +static WEBP_INLINE int AddSubtractComponentFullWithCoefficient( + int a, int b, int c) { + return Clip255(a + ((b - c) >> 2)); +} + +static WEBP_INLINE uint32_t ClampedAddSubtractFullWithCoefficient( + uint32_t c0, uint32_t c1, uint32_t c2) { + const int a = AddSubtractComponentFullWithCoefficient( + c0 >> 24, c1 >> 24, c2 >> 24); + const int r = AddSubtractComponentFullWithCoefficient((c0 >> 16) & 0xff, + (c1 >> 16) & 0xff, + (c2 >> 16) & 0xff); + const int g = AddSubtractComponentFullWithCoefficient((c0 >> 8) & 0xff, + (c1 >> 8) & 0xff, + (c2 >> 8) & 0xff); + const int b = AddSubtractComponentFullWithCoefficient( + c0 & 0xff, c1 & 0xff, c2 & 0xff); + return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b; +} + +//------------------------------------------------------------------------------ + +// Find palette entry with minimum error from difference of actual pixel value +// and predicted pixel value. Propagate error of pixel to its top and left pixel +// in src array. Write predicted_value + palette_entry to new_image. Return +// index of best palette entry. +static int FindBestPaletteEntry(uint32_t src, uint32_t predicted_value, + const uint32_t palette[], int palette_size) { + int i; + int idx = 0; + uint32_t best_distance = CalcDist(predicted_value, src, palette[0]); + for (i = 1; i < palette_size; ++i) { + const uint32_t distance = CalcDist(predicted_value, src, palette[i]); + if (distance < best_distance) { + best_distance = distance; + idx = i; + } + } + return idx; +} + +static void ApplyBestPaletteEntry(int x, int y, + uint32_t new_value, uint32_t palette_value, + uint32_t* src, int src_stride, + uint32_t* new_image) { + AddPixelsEq(&new_value, palette_value); + if (x > 0) { + src[x - 1] = ClampedAddSubtractFullWithCoefficient(src[x - 1], + new_value, src[x]); + } + if (y > 0) { + src[x - src_stride] = + ClampedAddSubtractFullWithCoefficient(src[x - src_stride], + new_value, src[x]); + } + new_image[x] = new_value; +} + +//------------------------------------------------------------------------------ +// Main entry point + +static WebPEncodingError ApplyDeltaPalette(uint32_t* src, uint32_t* dst, + uint32_t src_stride, + uint32_t dst_stride, + const uint32_t* palette, + int palette_size, + int width, int height, + int num_passes) { + int x, y; + WebPEncodingError err = VP8_ENC_OK; + uint32_t* new_image = (uint32_t*)WebPSafeMalloc(width, sizeof(*new_image)); + uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); + if (new_image == NULL || tmp_row == NULL) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } + + while (num_passes--) { + uint32_t* cur_src = src; + uint32_t* cur_dst = dst; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + const uint32_t predicted_value = Predict(x, y, new_image); + tmp_row[x] = FindBestPaletteEntry(cur_src[x], predicted_value, + palette, palette_size); + ApplyBestPaletteEntry(x, y, predicted_value, palette[tmp_row[x]], + cur_src, src_stride, new_image); + } + for (x = 0; x < width; ++x) { + cur_dst[x] = palette[tmp_row[x]]; + } + cur_src += src_stride; + cur_dst += dst_stride; + } + } + Error: + WebPSafeFree(new_image); + WebPSafeFree(tmp_row); + return err; +} + +// replaces enc->argb_ by a palettizable approximation of it, +// and generates optimal enc->palette_[] +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { + const WebPPicture* const pic = enc->pic_; + uint32_t* src = pic->argb; + uint32_t* dst = enc->argb_; + const int width = pic->width; + const int height = pic->height; + + WebPEncodingError err = VP8_ENC_OK; + memcpy(enc->palette_, kDeltaPalette, sizeof(kDeltaPalette)); + enc->palette_[DELTA_PALETTE_SIZE - 1] = src[0] - 0xff000000u; + enc->palette_size_ = DELTA_PALETTE_SIZE; + err = ApplyDeltaPalette(src, dst, pic->argb_stride, enc->current_width_, + enc->palette_, enc->palette_size_, + width, height, 2); + if (err != VP8_ENC_OK) goto Error; + + Error: + return err; +} + #else // !WEBP_EXPERIMENTAL_FEATURES -void delta_palettization_stub(void); -void delta_palettization_stub(void) {} +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc) { + (void)enc; + return VP8_ENC_ERROR_INVALID_CONFIGURATION; +} #endif // WEBP_EXPERIMENTAL_FEATURES diff --git a/src/enc/delta_palettization.h b/src/enc/delta_palettization.h index dbaf43c7..e41c0c5a 100644 --- a/src/enc/delta_palettization.h +++ b/src/enc/delta_palettization.h @@ -13,26 +13,13 @@ #ifndef WEBP_ENC_DELTA_PALETTIZATION_H_ #define WEBP_ENC_DELTA_PALETTIZATION_H_ -#ifdef WEBP_EXPERIMENTAL_FEATURES +#include "../webp/encode.h" +#include "../enc/vp8li.h" -#include "../webp/types.h" - -// Format allows palette up to 256 entries, but more palette entries produce -// bigger entropy. In the future it will probably be useful to add more entries -// that are far from the origin of the palette or choose remaining entries -// dynamically. -#define DELTA_PALETTE_SIZE 226 - -extern const uint32_t kDeltaPalette[DELTA_PALETTE_SIZE]; - -// Find palette entry with minimum error from difference of actual pixel value -// and predicted pixel value. Propagate error of pixel to its top and left pixel -// in src array. Write predicted_value + palette_entry to new_image. Return -// index of best palette entry. -int FindBestPaletteEntry(int x, int y, const uint32_t* palette, - int palette_size, uint32_t* src, - int src_stride, uint32_t* new_image); - -#endif // WEBP_EXPERIMENTAL_FEATURES +// Replaces enc->argb_[] input by a palettizable approximation of it, +// and generates optimal enc->palette_[]. +// This function can revert enc->use_palette_ / enc->use_predict_ flag +// if delta-palettization is not producing expected saving. +WebPEncodingError WebPSearchOptimalDeltaPalette(VP8LEncoder* const enc); #endif // WEBP_ENC_DELTA_PALETTIZATION_H_ diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 784c39ff..978db379 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -1046,42 +1046,6 @@ static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc, quality); } - -#ifdef WEBP_EXPERIMENTAL_FEATURES - -static WebPEncodingError ApplyDeltaPalette(uint32_t* src, uint32_t* dst, - uint32_t src_stride, - uint32_t dst_stride, - const uint32_t* palette, - int palette_size, - int width, int height, - int xbits, uint8_t* row, - int num_passes) { - int x, y; - WebPEncodingError err = VP8_ENC_OK; - uint32_t* new_image = (uint32_t*)WebPSafeMalloc(width, sizeof(*new_image)); - if (new_image == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; - - while (num_passes--) { - uint32_t* cur_src = src; - uint32_t* cur_dst = dst; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - row[x] = FindBestPaletteEntry(x, y, palette, palette_size, cur_src, - src_stride, new_image); - } - VP8LBundleColorMap(row, width, xbits, cur_dst); - cur_src += src_stride; - cur_dst += dst_stride; - } - } - - WebPSafeFree(new_image); - return err; -} - -#endif // WEBP_EXPERIMENTAL_FEATURES - // ----------------------------------------------------------------------------- static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, @@ -1151,15 +1115,21 @@ static WebPEncodingError WriteImage(const WebPPicture* const pic, // Allocates the memory for argb (W x H) buffer, 2 rows of context for // prediction and transform data. +// Flags influencing the memory allocated: +// enc->transform_bits_ +// enc->use_predict_, enc->use_cross_color_ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, int width, int height) { WebPEncodingError err = VP8_ENC_OK; + if (enc->argb_ == NULL) { const int tile_size = 1 << enc->transform_bits_; const uint64_t image_size = width * height; - const uint64_t argb_scratch_size = tile_size * width + width; + const uint64_t argb_scratch_size = + enc->use_predict_ ? tile_size * width + width : 0; const int transform_data_size = + (enc->use_predict_ || enc->use_cross_color_) ? VP8LSubSampleSize(width, enc->transform_bits_) * - VP8LSubSampleSize(height, enc->transform_bits_); + VP8LSubSampleSize(height, enc->transform_bits_) : 0; const uint64_t total_size = image_size + argb_scratch_size + (uint64_t)transform_data_size; uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem)); @@ -1167,17 +1137,42 @@ static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } + // TODO(skal): align enc->argb_ = mem; mem += image_size; enc->argb_scratch_ = mem; mem += argb_scratch_size; enc->transform_data_ = mem; enc->current_width_ = width; - + } Error: return err; } +static void ClearTransformBuffer(VP8LEncoder* const enc) { + WebPSafeFree(enc->argb_); + enc->argb_ = NULL; +} + +static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { + WebPEncodingError err = VP8_ENC_OK; + const WebPPicture* const picture = enc->pic_; + const int width = picture->width; + const int height = picture->height; + int y; + err = AllocateTransformBuffer(enc, width, height); + if (err != VP8_ENC_OK) return err; + for (y = 0; y < height; ++y) { + memcpy(enc->argb_ + y * width, + picture->argb + y * picture->argb_stride, + width * sizeof(*enc->argb_)); + } + assert(enc->current_width_ == width); + return VP8_ENC_OK; +} + +// ----------------------------------------------------------------------------- + static void MapToPalette(const uint32_t palette[], int num_colors, uint32_t* const last_pix, int* const last_idx, const uint32_t* src, uint8_t* dst, int width) { @@ -1202,12 +1197,21 @@ static void MapToPalette(const uint32_t palette[], int num_colors, *last_pix = prev_pix; } -static void ApplyPalette(uint32_t* src, uint32_t* dst, - uint32_t src_stride, uint32_t dst_stride, +// Remap argb values in src[] to packed palettes entries in dst[] +// using 'row' as a temporary buffer of size 'width'. +// We assume that all src[] values have a corresponding entry in the palette. +// Note: src[] can be the same as dst[] +static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride, + uint32_t* dst, uint32_t dst_stride, const uint32_t* palette, int palette_size, - int width, int height, int xbits, uint8_t* row) { + int width, int height, int xbits) { + // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be + // made to work in-place. + uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row)); int i, x, y; int use_LUT = 1; + + if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; for (i = 0; i < palette_size; ++i) { if ((palette[i] & 0xffff00ffu) != 0) { use_LUT = 0; @@ -1224,9 +1228,9 @@ static void ApplyPalette(uint32_t* src, uint32_t* dst, for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { const int color = (src[x] >> 8) & 0xff; - row[x] = inv_palette[color]; + tmp_row[x] = inv_palette[color]; } - VP8LBundleColorMap(row, width, xbits, dst); + VP8LBundleColorMap(tmp_row, width, xbits, dst); src += src_stride; dst += dst_stride; } @@ -1236,36 +1240,29 @@ static void ApplyPalette(uint32_t* src, uint32_t* dst, int last_idx = 0; for (y = 0; y < height; ++y) { MapToPalette(palette, palette_size, &last_pix, &last_idx, - src, row, width); - VP8LBundleColorMap(row, width, xbits, dst); + src, tmp_row, width); + VP8LBundleColorMap(tmp_row, width, xbits, dst); src += src_stride; dst += dst_stride; } } + WebPSafeFree(tmp_row); + return VP8_ENC_OK; } // Note: Expects "enc->palette_" to be set properly. -// Also, "enc->palette_" will be modified after this call and should not be used -// later. -static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, - VP8LEncoder* const enc, - int use_delta_palette) { +static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc, + int in_place) { WebPEncodingError err = VP8_ENC_OK; - int i; const WebPPicture* const pic = enc->pic_; - uint32_t* src = pic->argb; - uint32_t* dst; const int width = pic->width; const int height = pic->height; - uint32_t* const palette = enc->palette_; + const uint32_t* const palette = enc->palette_; + const uint32_t* src = in_place ? enc->argb_ : pic->argb; + const int src_stride = in_place ? enc->current_width_ : pic->argb_stride; const int palette_size = enc->palette_size_; - uint8_t* row = NULL; int xbits; -#ifndef WEBP_EXPERIMENTAL_FEATURES - (void)use_delta_palette; -#endif // WEBP_EXPERIMENTAL_FEATURES - // Replace each input pixel by corresponding palette index. // This is done line by line. if (palette_size <= 4) { @@ -1275,54 +1272,45 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, } err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); - if (err != VP8_ENC_OK) goto Error; - dst = enc->argb_; + if (err != VP8_ENC_OK) return err; - row = (uint8_t*)WebPSafeMalloc(width, sizeof(*row)); - if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; + err = ApplyPalette(src, src_stride, + enc->argb_, enc->current_width_, + palette, palette_size, width, height, xbits); + return err; +} -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (use_delta_palette) { - ApplyDeltaPalette(src, dst, pic->argb_stride, enc->current_width_, - palette, palette_size, width, height, xbits, row, 2); - } else { -#endif // WEBP_EXPERIMENTAL_FEATURES - ApplyPalette(src, dst, pic->argb_stride, enc->current_width_, - palette, palette_size, width, height, xbits, row); -#ifdef WEBP_EXPERIMENTAL_FEATURES - } -#endif // WEBP_EXPERIMENTAL_FEATURES - - // Save palette to bitstream. +// Save palette_[] to bitstream. +static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, + VP8LEncoder* const enc) { + int i; + uint32_t tmp_palette[MAX_PALETTE_SIZE]; + const int palette_size = enc->palette_size_; + const uint32_t* const palette = enc->palette_; VP8LPutBits(bw, TRANSFORM_PRESENT, 1); VP8LPutBits(bw, COLOR_INDEXING_TRANSFORM, 2); - assert(palette_size >= 1); + assert(palette_size >= 1 && palette_size <= MAX_PALETTE_SIZE); VP8LPutBits(bw, palette_size - 1, 8); for (i = palette_size - 1; i >= 1; --i) { - palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); + tmp_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); } - err = EncodeImageNoHuffman(bw, palette, &enc->hash_chain_, enc->refs_, + tmp_palette[0] = palette[0]; + return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_, enc->refs_, palette_size, 1, 20 /* quality */); - Error: - WebPSafeFree(row); - return err; } #ifdef WEBP_EXPERIMENTAL_FEATURES -static WebPEncodingError EncodeDeltaPalettizedImage(VP8LBitWriter* const bw, - VP8LEncoder* const enc, - int quality) { +static WebPEncodingError EncodeDeltaPalettePredictorImage( + VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality) { const WebPPicture* const pic = enc->pic_; - uint32_t* src = pic->argb; const int width = pic->width; const int height = pic->height; - uint8_t* row = NULL; const int pred_bits = 5; const int transform_width = VP8LSubSampleSize(width, pred_bits); const int transform_height = VP8LSubSampleSize(height, pred_bits); - const int pred = 7; + const int pred = 7; // default is Predictor7 (Top/Left Average) const int tiles_per_row = VP8LSubSampleSize(width, pred_bits); const int tiles_per_col = VP8LSubSampleSize(height, pred_bits); uint32_t* predictors; @@ -1331,10 +1319,7 @@ static WebPEncodingError EncodeDeltaPalettizedImage(VP8LBitWriter* const bw, predictors = (uint32_t*)WebPSafeMalloc(tiles_per_col * tiles_per_row, sizeof(*predictors)); - if (predictors == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } + if (predictors == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY; for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { @@ -1349,15 +1334,6 @@ static WebPEncodingError EncodeDeltaPalettizedImage(VP8LBitWriter* const bw, (VP8LBackwardRefs*)enc->refs_, // cast const away transform_width, transform_height, quality); - if (err != VP8_ENC_OK) goto Error; - - memcpy(enc->palette_, kDeltaPalette, sizeof(kDeltaPalette)); - enc->palette_[DELTA_PALETTE_SIZE - 1] = src[0] - 0xff000000u; - enc->palette_size_ = DELTA_PALETTE_SIZE; - EncodePalette(bw, enc, 1); - - Error: - WebPSafeFree(row); WebPSafeFree(predictors); return err; } @@ -1387,7 +1363,7 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) { VP8LHashChainClear(&enc->hash_chain_); VP8LBackwardRefsClear(&enc->refs_[0]); VP8LBackwardRefsClear(&enc->refs_[1]); - WebPSafeFree(enc->argb_); + ClearTransformBuffer(enc); WebPSafeFree(enc); } } @@ -1408,6 +1384,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, int use_near_lossless = 0; int hdr_size = 0; int data_size = 0; + int use_delta_palettization = 0; if (enc == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; @@ -1438,26 +1415,32 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, enc->use_cross_color_ = 0; enc->use_subtract_green_ = 0; enc->use_palette_ = 1; - EncodeDeltaPalettizedImage(bw, enc, quality); - } else { -#endif // WEBP_EXPERIMENTAL_FEATURES + err = MakeInputImageCopy(enc); + if (err != VP8_ENC_OK) goto Error; + err = WebPSearchOptimalDeltaPalette(enc); + if (err != VP8_ENC_OK) goto Error; if (enc->use_palette_) { - err = EncodePalette(bw, enc, 0); - if (err != VP8_ENC_OK) goto Error; - } - - // In case image is not packed. - if (enc->argb_ == NULL) { - int y; err = AllocateTransformBuffer(enc, width, height); if (err != VP8_ENC_OK) goto Error; - assert(enc->argb_ != NULL); - for (y = 0; y < height; ++y) { - memcpy(enc->argb_ + y * width, - picture->argb + y * picture->argb_stride, - width * sizeof(*enc->argb_)); - } - enc->current_width_ = width; + err = EncodeDeltaPalettePredictorImage(bw, enc, quality); + if (err != VP8_ENC_OK) goto Error; + use_delta_palettization = 1; + } + } +#endif // WEBP_EXPERIMENTAL_FEATURES + + // Encode palette + if (enc->use_palette_) { + err = EncodePalette(bw, enc); + if (err != VP8_ENC_OK) goto Error; + err = MapImageFromPalette(enc, use_delta_palettization); + if (err != VP8_ENC_OK) goto Error; + } + if (!use_delta_palettization) { + // In case image is not packed. + if (enc->argb_ == NULL) { + err = MakeInputImageCopy(enc); + if (err != VP8_ENC_OK) goto Error; } // ------------------------------------------------------------------------- @@ -1478,9 +1461,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, height, quality, bw); if (err != VP8_ENC_OK) goto Error; } -#ifdef WEBP_EXPERIMENTAL_FEATURES } -#endif // WEBP_EXPERIMENTAL_FEATURES VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms.