From 169004b1d558c4767f110dde8f7824e59f9f3024 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Wed, 18 May 2016 14:27:15 -0700 Subject: [PATCH 1/2] Refactor GetColorPalette method. This was defined (slightly differently) at two places. Created a common method and moved to utils/utils.[hc]. Change-Id: I19adc9c48f2a4e2ec9d995e78add6f25172774c2 --- src/enc/vp8l.c | 50 ++------------------------------ src/mux/anim_encode.c | 60 +++------------------------------------ src/utils/utils.c | 66 +++++++++++++++++++++++++++++++++++++++++++ src/utils/utils.h | 13 +++++++++ 4 files changed, 85 insertions(+), 104 deletions(-) diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 2c308577..3aac2fdd 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -126,54 +126,8 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic, int low_effort, uint32_t palette[MAX_PALETTE_SIZE], int* const palette_size) { - int i, x, y, key; - int num_colors = 0; - uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; - uint32_t colors[MAX_PALETTE_SIZE * 4]; - static const uint32_t kHashMul = 0x1e35a7bd; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT; - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors > MAX_PALETTE_SIZE) { - return 0; - } - break; - } else if (colors[key] == last_pix) { - // The color is already there. - break; - } else { - // Some other color sits there. - // Do linear conflict resolution. - ++key; - key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. - } - } - } - argb += pic->argb_stride; - } - - // TODO(skal): could we reuse in_use[] to speed up EncodePalette()? - num_colors = 0; - for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { - if (in_use[i]) { - palette[num_colors] = colors[i]; - ++num_colors; - } - } + const int num_colors = WebPGetColorPalette(pic, palette); + if (num_colors > MAX_PALETTE_SIZE) return 0; *palette_size = num_colors; qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { diff --git a/src/mux/anim_encode.c b/src/mux/anim_encode.c index 79aaa87c..09789d4e 100644 --- a/src/mux/anim_encode.c +++ b/src/mux/anim_encode.c @@ -645,61 +645,6 @@ static int IsLossyBlendingPossible(const WebPPicture* const src, return 1; } -#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold. -#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold. -#define MAX_COLOR_COUNT 256 // Power of 2 greater than MAX_COLORS_LOSSLESS. -#define HASH_SIZE (MAX_COLOR_COUNT * 4) -#define HASH_RIGHT_SHIFT 22 // 32 - log2(HASH_SIZE). - -// TODO(urvang): Also used in enc/vp8l.c. Move to utils. -// If the number of colors in the 'pic' is at least MAX_COLOR_COUNT, return -// MAX_COLOR_COUNT. Otherwise, return the exact number of colors in the 'pic'. -static int GetColorCount(const WebPPicture* const pic) { - int x, y; - int num_colors = 0; - uint8_t in_use[HASH_SIZE] = { 0 }; - uint32_t colors[HASH_SIZE]; - static const uint32_t kHashMul = 0x1e35a7bd; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - int key; - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = (kHashMul * last_pix) >> HASH_RIGHT_SHIFT; - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors >= MAX_COLOR_COUNT) { - return MAX_COLOR_COUNT; // Exact count not needed. - } - break; - } else if (colors[key] == last_pix) { - break; // The color is already there. - } else { - // Some other color sits here, so do linear conflict resolution. - ++key; - key &= (HASH_SIZE - 1); // Key mask. - } - } - } - argb += pic->argb_stride; - } - return num_colors; -} - -#undef MAX_COLOR_COUNT -#undef HASH_SIZE -#undef HASH_RIGHT_SHIFT - // For pixels in 'rect', replace those pixels in 'dst' that are same as 'src' by // transparent pixels. static void IncreaseTransparency(const WebPPicture* const src, @@ -856,6 +801,9 @@ enum { CANDIDATE_COUNT }; +#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold. +#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold. + // Generates candidates for a given dispose method given pre-filled sub-frame // 'params'. static WebPEncodingError GenerateCandidates( @@ -890,7 +838,7 @@ static WebPEncodingError GenerateCandidates( candidate_ll->evaluate_ = is_lossless; candidate_lossy->evaluate_ = !is_lossless; } else { // Use a heuristic for trying lossless and/or lossy compression. - const int num_colors = GetColorCount(¶ms->sub_frame_ll_); + const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll_, NULL); candidate_ll->evaluate_ = (num_colors < MAX_COLORS_LOSSLESS); candidate_lossy->evaluate_ = (num_colors >= MIN_COLORS_LOSSY); } diff --git a/src/utils/utils.c b/src/utils/utils.c index d8e30930..2602ca3c 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -15,6 +15,7 @@ #include // for memcpy() #include "../webp/decode.h" #include "../webp/encode.h" +#include "../webp/format_constants.h" // for MAX_PALETTE_SIZE #include "./utils.h" // If PRINT_MEM_INFO is defined, extra info (like total memory used, number of @@ -237,3 +238,68 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { } //------------------------------------------------------------------------------ + +#define MAX_COLOR_COUNT MAX_PALETTE_SIZE +#define COLOR_HASH_SIZE (MAX_COLOR_COUNT * 4) +#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE). + +int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { + int i; + int x, y; + int num_colors = 0; + uint8_t in_use[COLOR_HASH_SIZE] = { 0 }; + uint32_t colors[COLOR_HASH_SIZE]; + static const uint32_t kHashMul = 0x1e35a7bdU; + const uint32_t* argb = pic->argb; + const int width = pic->width; + const int height = pic->height; + uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] + assert(pic != NULL); + assert(pic->use_argb); + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + int key; + if (argb[x] == last_pix) { + continue; + } + last_pix = argb[x]; + key = (kHashMul * last_pix) >> COLOR_HASH_RIGHT_SHIFT; + while (1) { + if (!in_use[key]) { + colors[key] = last_pix; + in_use[key] = 1; + ++num_colors; + if (num_colors > MAX_COLOR_COUNT) { + return MAX_COLOR_COUNT + 1; // Exact count not needed. + } + break; + } else if (colors[key] == last_pix) { + break; // The color is already there. + } else { + // Some other color sits here, so do linear conflict resolution. + ++key; + key &= (COLOR_HASH_SIZE - 1); // Key mask. + } + } + } + argb += pic->argb_stride; + } + + if (palette != NULL) { // Fill the colors into palette. + num_colors = 0; + for (i = 0; i < COLOR_HASH_SIZE; ++i) { + if (in_use[i]) { + palette[num_colors] = colors[i]; + ++num_colors; + } + } + } + return num_colors; +} + +#undef MAX_COLOR_COUNT +#undef COLOR_HASH_SIZE +#undef COLOR_HASH_RIGHT_SHIFT + +//------------------------------------------------------------------------------ diff --git a/src/utils/utils.h b/src/utils/utils.h index c6c10149..e0a81126 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -160,6 +160,19 @@ WEBP_EXTERN(void) WebPCopyPlane(const uint8_t* src, int src_stride, WEBP_EXTERN(void) WebPCopyPixels(const struct WebPPicture* const src, struct WebPPicture* const dst); +//------------------------------------------------------------------------------ +// Unique colors. + +// Returns count of unique colors in 'pic', assuming pic->use_argb is true. +// If the unique color count is more than MAX_COLOR_COUNT, returns +// MAX_COLOR_COUNT+1. +// If 'palette' is not NULL and number of unique colors is less than or equal to +// MAX_COLOR_COUNT, also outputs the actual unique colors into 'palette'. +// Note: 'palette' is assumed to be an array already allocated with at least +// MAX_COLOR_COUNT elements. +WEBP_EXTERN(int) WebPGetColorPalette(const struct WebPPicture* const pic, + uint32_t* const palette); + //------------------------------------------------------------------------------ #ifdef __cplusplus From f25c4406e662ec09d77e2bc7e0731789aa972998 Mon Sep 17 00:00:00 2001 From: Urvang Joshi Date: Fri, 20 May 2016 15:07:50 -0700 Subject: [PATCH 2/2] WebPAnimEncoder: Restore original canvas between multiple encodes. tl;dr We do the following: - Start with transparent value of 0x00000000 instead of 0x00ffffff, so that WebPCleanupTransparentAreaLossless() is a no-op. - Restore the original canvas after lossy encoding, to discard changes made by WebPCleanupTransparentArea() before the next encode. Explanation of why: In the mixed mode, anim_encoder tries to encode using both lossless and lossy compression. In fact, when "min_size" option is enabled, there are at most 4 encodes that can happen in this order: - lossless with dispose none - lossy with dispose none - lossless with dispose background - lossy with dispose background But both lossless and lossy both potentially modify the canvas during encode (for better compression): - Lossless: WebPCleanupTransparentAreaLossless() turns all transparent pixels to 0x00000000 - Lossy: WebPCleanupTransparentArea() flattens some transparent pixels So, the result is that, sometimes we feed the modified canvas to the encoder instead of the original one, which isn't the right thing to do. This also applies to just lossless or just lossy encoding, as multiple encodes happen (with the two dispose methods) in those cases too. Change-Id: Idfa8ce831a1627014785ba7d0316c42f72594455 --- src/mux/anim_encode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mux/anim_encode.c b/src/mux/anim_encode.c index 09789d4e..4d8feb58 100644 --- a/src/mux/anim_encode.c +++ b/src/mux/anim_encode.c @@ -190,7 +190,8 @@ int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options, return 1; } -#define TRANSPARENT_COLOR 0x00ffffff +// This starting value is more fit to WebPCleanupTransparentAreaLossless(). +#define TRANSPARENT_COLOR 0x00000000 static void ClearRectangle(WebPPicture* const picture, int left, int top, int width, int height) { @@ -865,6 +866,7 @@ static WebPEncodingError GenerateCandidates( EncodeCandidate(¶ms->sub_frame_lossy_, ¶ms->rect_lossy_, config_lossy, use_blending_lossy, candidate_lossy); if (error_code != VP8_ENC_OK) return error_code; + enc->curr_canvas_copy_modified_ = 1; } return error_code; }