Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Unified Diff: third_party/libwebp/enc/vp8l_enc.c

Issue 2651883004: libwebp-0.6.0-rc1 (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/libwebp/enc/vp8l.c ('k') | third_party/libwebp/enc/vp8li.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/libwebp/enc/vp8l_enc.c
diff --git a/third_party/libwebp/enc/vp8l.c b/third_party/libwebp/enc/vp8l_enc.c
similarity index 89%
rename from third_party/libwebp/enc/vp8l.c
rename to third_party/libwebp/enc/vp8l_enc.c
index e4ad2959b825c3bb8367d2a54deec3fd4801a203..b1a793d956e379d4820bb6046009853508a0c607 100644
--- a/third_party/libwebp/enc/vp8l.c
+++ b/third_party/libwebp/enc/vp8l_enc.c
@@ -15,17 +15,18 @@
#include <assert.h>
#include <stdlib.h>
-#include "./backward_references.h"
-#include "./histogram.h"
-#include "./vp8enci.h"
-#include "./vp8li.h"
+#include "./backward_references_enc.h"
+#include "./histogram_enc.h"
+#include "./vp8i_enc.h"
+#include "./vp8li_enc.h"
#include "../dsp/lossless.h"
-#include "../utils/bit_writer.h"
-#include "../utils/huffman_encode.h"
+#include "../dsp/lossless_common.h"
+#include "../utils/bit_writer_utils.h"
+#include "../utils/huffman_encode_utils.h"
#include "../utils/utils.h"
#include "../webp/format_constants.h"
-#include "./delta_palettization.h"
+#include "./delta_palettization_enc.h"
#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
// Maximum number of histogram images (sub-blocks).
@@ -163,18 +164,25 @@ typedef enum {
kHistoTotal // Must be last.
} HistoIx;
-static void AddSingleSubGreen(uint32_t p, uint32_t* r, uint32_t* b) {
- const uint32_t green = p >> 8; // The upper bits are masked away later.
+static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) {
+ const int green = p >> 8; // The upper bits are masked away later.
++r[((p >> 16) - green) & 0xff];
- ++b[(p - green) & 0xff];
+ ++b[((p >> 0) - green) & 0xff];
}
static void AddSingle(uint32_t p,
- uint32_t* a, uint32_t* r, uint32_t* g, uint32_t* b) {
- ++a[p >> 24];
+ uint32_t* const a, uint32_t* const r,
+ uint32_t* const g, uint32_t* const b) {
+ ++a[(p >> 24) & 0xff];
++r[(p >> 16) & 0xff];
- ++g[(p >> 8) & 0xff];
- ++b[(p & 0xff)];
+ ++g[(p >> 8) & 0xff];
+ ++b[(p >> 0) & 0xff];
+}
+
+static WEBP_INLINE uint32_t HashPix(uint32_t pix) {
+ // Note that masking with 0xffffffffu is for preventing an
+ // 'unsigned int overflow' warning. Doesn't impact the compiled code.
+ return ((((uint64_t)pix + (pix >> 19)) * 0x39c5fba7ull) & 0xffffffffu) >> 24;
}
static int AnalyzeEntropy(const uint32_t* argb,
@@ -214,8 +222,8 @@ static int AnalyzeEntropy(const uint32_t* argb,
&histo[kHistoBluePredSubGreen * 256]);
{
// Approximate the palette by the entropy of the multiplicative hash.
- const int hash = ((pix + (pix >> 19)) * 0x39c5fba7) >> 24;
- ++histo[kHistoPalette * 256 + (hash & 0xff)];
+ const uint32_t hash = HashPix(pix);
+ ++histo[kHistoPalette * 256 + hash];
}
}
prev_row = curr_row;
@@ -311,7 +319,10 @@ static int GetHistoBits(int method, int use_palette, int width, int height) {
static int GetTransformBits(int method, int histo_bits) {
const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5;
- return (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
+ const int res =
+ (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
+ assert(res <= MAX_TRANSFORM_BITS);
+ return res;
}
static int AnalyzeAndInit(VP8LEncoder* const enc) {
@@ -696,7 +707,7 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
VP8LHashChain* const hash_chain,
VP8LBackwardRefs refs_array[2],
int width, int height,
- int quality) {
+ int quality, int low_effort) {
int i;
int max_tokens = 0;
WebPEncodingError err = VP8_ENC_OK;
@@ -714,7 +725,8 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
}
// Calculate backward references from ARGB image.
- if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) {
+ if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort)) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
@@ -814,11 +826,18 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
goto Error;
}
- *cache_bits = use_cache ? MAX_COLOR_CACHE_BITS : 0;
+ if (use_cache) {
+ // If the value is different from zero, it has been set during the
+ // palette analysis.
+ if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS;
+ } else {
+ *cache_bits = 0;
+ }
// 'best_refs' is the reference to the best backward refs and points to one
// of refs_array[0] or refs_array[1].
// Calculate backward references from ARGB image.
- if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) {
+ if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort)) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
@@ -899,7 +918,7 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw,
err = EncodeImageNoHuffman(bw, histogram_argb, hash_chain, refs_array,
VP8LSubSampleSize(width, histogram_bits),
VP8LSubSampleSize(height, histogram_bits),
- quality);
+ quality, low_effort);
WebPSafeFree(histogram_argb);
if (err != VP8_ENC_OK) goto Error;
}
@@ -990,12 +1009,12 @@ static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
(VP8LHashChain*)&enc->hash_chain_,
(VP8LBackwardRefs*)enc->refs_, // cast const away
transform_width, transform_height,
- quality);
+ quality, low_effort);
}
static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
int width, int height,
- int quality,
+ int quality, int low_effort,
VP8LBitWriter* const bw) {
const int ccolor_transform_bits = enc->transform_bits_;
const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
@@ -1011,7 +1030,7 @@ static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
(VP8LHashChain*)&enc->hash_chain_,
(VP8LBackwardRefs*)enc->refs_, // cast const away
transform_width, transform_height,
- quality);
+ quality, low_effort);
}
// -----------------------------------------------------------------------------
@@ -1156,7 +1175,8 @@ static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
// -----------------------------------------------------------------------------
-static int SearchColor(const uint32_t sorted[], uint32_t color, int hi) {
+static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
+ int hi) {
int low = 0;
if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
while (1) {
@@ -1171,35 +1191,68 @@ static int SearchColor(const uint32_t sorted[], uint32_t color, int hi) {
}
}
+#define APPLY_PALETTE_GREEDY_MAX 4
+
+static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[],
+ int palette_size,
+ uint32_t color) {
+ (void)palette_size;
+ assert(palette_size < APPLY_PALETTE_GREEDY_MAX);
+ assert(3 == APPLY_PALETTE_GREEDY_MAX - 1);
+ if (color == palette[0]) return 0;
+ if (color == palette[1]) return 1;
+ if (color == palette[2]) return 2;
+ return 3;
+}
+
+static WEBP_INLINE uint32_t ApplyPaletteHash0(uint32_t color) {
+ // Focus on the green color.
+ return (color >> 8) & 0xff;
+}
+
+#define PALETTE_INV_SIZE_BITS 11
+#define PALETTE_INV_SIZE (1 << PALETTE_INV_SIZE_BITS)
+
+static WEBP_INLINE uint32_t ApplyPaletteHash1(uint32_t color) {
+ // Forget about alpha.
+ return ((color & 0x00ffffffu) * 4222244071u) >> (32 - PALETTE_INV_SIZE_BITS);
+}
+
+static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
+ // Forget about alpha.
+ return (color & 0x00ffffffu) * ((1u << 31) - 1) >>
+ (32 - PALETTE_INV_SIZE_BITS);
+}
+
// Sort palette in increasing order and prepare an inverse mapping array.
static void PrepareMapToPalette(const uint32_t palette[], int num_colors,
- uint32_t sorted[], int idx_map[]) {
+ uint32_t sorted[], uint32_t idx_map[]) {
int i;
memcpy(sorted, palette, num_colors * sizeof(*sorted));
qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
for (i = 0; i < num_colors; ++i) {
- idx_map[SearchColor(sorted, palette[i], num_colors)] = i;
+ idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
}
}
-static void MapToPalette(const uint32_t sorted_palette[], int num_colors,
- uint32_t* const last_pix, int* const last_idx,
- const int idx_map[],
- const uint32_t* src, uint8_t* dst, int width) {
- int x;
- int prev_idx = *last_idx;
- uint32_t prev_pix = *last_pix;
- for (x = 0; x < width; ++x) {
- const uint32_t pix = src[x];
- if (pix != prev_pix) {
- prev_idx = idx_map[SearchColor(sorted_palette, pix, num_colors)];
- prev_pix = pix;
- }
- dst[x] = prev_idx;
- }
- *last_idx = prev_idx;
- *last_pix = prev_pix;
-}
+// Use 1 pixel cache for ARGB pixels.
+#define APPLY_PALETTE_FOR(COLOR_INDEX) do { \
+ uint32_t prev_pix = palette[0]; \
+ uint32_t prev_idx = 0; \
+ for (y = 0; y < height; ++y) { \
+ for (x = 0; x < width; ++x) { \
+ const uint32_t pix = src[x]; \
+ if (pix != prev_pix) { \
+ prev_idx = COLOR_INDEX; \
+ prev_pix = pix; \
+ } \
+ tmp_row[x] = prev_idx; \
+ } \
+ VP8LBundleColorMap(tmp_row, width, xbits, dst); \
+ src += src_stride; \
+ dst += dst_stride; \
+ } \
+} while (0)
// Remap argb values in src[] to packed palettes entries in dst[]
// using 'row' as a temporary buffer of size 'width'.
@@ -1212,52 +1265,59 @@ static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
// 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;
+ int x, y;
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;
- break;
- }
- }
- if (use_LUT) {
- uint8_t inv_palette[MAX_PALETTE_SIZE] = { 0 };
- for (i = 0; i < palette_size; ++i) {
- const int color = (palette[i] >> 8) & 0xff;
- inv_palette[color] = i;
- }
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const int color = (src[x] >> 8) & 0xff;
- tmp_row[x] = inv_palette[color];
+ if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
+ APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix));
+ } else {
+ int i, j;
+ uint16_t buffer[PALETTE_INV_SIZE];
+ uint32_t (*const hash_functions[])(uint32_t) = {
+ ApplyPaletteHash0, ApplyPaletteHash1, ApplyPaletteHash2
+ };
+
+ // Try to find a perfect hash function able to go from a color to an index
+ // within 1 << PALETTE_INV_SIZE_BITS in order to build a hash map to go
+ // from color to index in palette.
+ for (i = 0; i < 3; ++i) {
+ int use_LUT = 1;
+ // Set each element in buffer to max uint16_t.
+ memset(buffer, 0xff, sizeof(buffer));
+ for (j = 0; j < palette_size; ++j) {
+ const uint32_t ind = hash_functions[i](palette[j]);
+ if (buffer[ind] != 0xffffu) {
+ use_LUT = 0;
+ break;
+ } else {
+ buffer[ind] = j;
+ }
}
- VP8LBundleColorMap(tmp_row, width, xbits, dst);
- src += src_stride;
- dst += dst_stride;
+ if (use_LUT) break;
}
- } else {
- // Use 1 pixel cache for ARGB pixels.
- uint32_t last_pix;
- int last_idx;
- uint32_t sorted[MAX_PALETTE_SIZE];
- int idx_map[MAX_PALETTE_SIZE];
- PrepareMapToPalette(palette, palette_size, sorted, idx_map);
- last_pix = palette[0];
- last_idx = 0;
- for (y = 0; y < height; ++y) {
- MapToPalette(sorted, palette_size, &last_pix, &last_idx,
- idx_map, src, tmp_row, width);
- VP8LBundleColorMap(tmp_row, width, xbits, dst);
- src += src_stride;
- dst += dst_stride;
+
+ if (i == 0) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash0(pix)]);
+ } else if (i == 1) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash1(pix)]);
+ } else if (i == 2) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash2(pix)]);
+ } else {
+ uint32_t idx_map[MAX_PALETTE_SIZE];
+ uint32_t palette_sorted[MAX_PALETTE_SIZE];
+ PrepareMapToPalette(palette, palette_size, palette_sorted, idx_map);
+ APPLY_PALETTE_FOR(
+ idx_map[SearchColorNoIdx(palette_sorted, pix, palette_size)]);
}
}
WebPSafeFree(tmp_row);
return VP8_ENC_OK;
}
+#undef APPLY_PALETTE_FOR
+#undef PALETTE_INV_SIZE_BITS
+#undef PALETTE_INV_SIZE
+#undef APPLY_PALETTE_GREEDY_MAX
// Note: Expects "enc->palette_" to be set properly.
static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
@@ -1290,7 +1350,7 @@ static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
}
// Save palette_[] to bitstream.
-static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
+static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
VP8LEncoder* const enc) {
int i;
uint32_t tmp_palette[MAX_PALETTE_SIZE];
@@ -1305,13 +1365,14 @@ static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
}
tmp_palette[0] = palette[0];
return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_, enc->refs_,
- palette_size, 1, 20 /* quality */);
+ palette_size, 1, 20 /* quality */, low_effort);
}
#ifdef WEBP_EXPERIMENTAL_FEATURES
static WebPEncodingError EncodeDeltaPalettePredictorImage(
- VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality) {
+ VP8LBitWriter* const bw, VP8LEncoder* const enc, int quality,
+ int low_effort) {
const WebPPicture* const pic = enc->pic_;
const int width = pic->width;
const int height = pic->height;
@@ -1342,7 +1403,7 @@ static WebPEncodingError EncodeDeltaPalettePredictorImage(
err = EncodeImageNoHuffman(bw, predictors, &enc->hash_chain_,
(VP8LBackwardRefs*)enc->refs_, // cast const away
transform_width, transform_height,
- quality);
+ quality, low_effort);
WebPSafeFree(predictors);
return err;
}
@@ -1393,7 +1454,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;
+ int use_delta_palette = 0;
if (enc == NULL) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
@@ -1420,7 +1481,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
}
#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (config->delta_palettization) {
+ if (config->use_delta_palette) {
enc->use_predict_ = 1;
enc->use_cross_color_ = 0;
enc->use_subtract_green_ = 0;
@@ -1432,21 +1493,25 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
if (enc->use_palette_) {
err = AllocateTransformBuffer(enc, width, height);
if (err != VP8_ENC_OK) goto Error;
- err = EncodeDeltaPalettePredictorImage(bw, enc, quality);
+ err = EncodeDeltaPalettePredictorImage(bw, enc, quality, low_effort);
if (err != VP8_ENC_OK) goto Error;
- use_delta_palettization = 1;
+ use_delta_palette = 1;
}
}
#endif // WEBP_EXPERIMENTAL_FEATURES
// Encode palette
if (enc->use_palette_) {
- err = EncodePalette(bw, enc);
+ err = EncodePalette(bw, low_effort, enc);
if (err != VP8_ENC_OK) goto Error;
- err = MapImageFromPalette(enc, use_delta_palettization);
+ err = MapImageFromPalette(enc, use_delta_palette);
if (err != VP8_ENC_OK) goto Error;
+ // If using a color cache, do not have it bigger than the number of colors.
+ if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
+ enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
+ }
}
- if (!use_delta_palettization) {
+ if (!use_delta_palette) {
// In case image is not packed.
if (enc->argb_ == NULL) {
err = MakeInputImageCopy(enc);
@@ -1468,7 +1533,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
if (enc->use_cross_color_) {
err = ApplyCrossColorFilter(enc, enc->current_width_,
- height, quality, bw);
+ height, quality, low_effort, bw);
if (err != VP8_ENC_OK) goto Error;
}
}
« no previous file with comments | « third_party/libwebp/enc/vp8l.c ('k') | third_party/libwebp/enc/vp8li.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698