From 00125196f3b75b307f77634b0670aa98689bcaf9 Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 8 Oct 2013 15:04:52 +0200 Subject: [PATCH] gif2webp: detect and flatten uniformly similar blocks helps during lossless compression. 10% average saving, but that's mostly on what was previously 'difficult' cases, where the gain is ~30-50% actually. Non-difficult cases are mostly unchanged. Tested over ~7k random web gifs. Change-Id: I09db4560e4ab09105d1cad28e6dbf83842eda8e9 --- examples/gif2webp.c | 1 + examples/gif2webp_util.c | 54 ++++++++++++++++++++++++++++++++++++++-- examples/gif2webp_util.h | 6 +++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/examples/gif2webp.c b/examples/gif2webp.c index cc44480e..a4f25156 100644 --- a/examples/gif2webp.c +++ b/examples/gif2webp.c @@ -170,6 +170,7 @@ static int OptimizeAndEncodeFrame( // For lossy compression, it's better to replace transparent pixels of // 'curr' with actual RGB values, whenever possible. WebPUtilReduceTransparency(prev_canvas, &rect, curr); + WebPUtilFlattenSimilarBlocks(prev_canvas, &rect, curr); } if (!WebPFrameCacheShouldTryKeyFrame(cache)) { // Add this as a frame rectangle. diff --git a/examples/gif2webp_util.c b/examples/gif2webp_util.c index 76209181..03b94bfe 100644 --- a/examples/gif2webp_util.c +++ b/examples/gif2webp_util.c @@ -290,10 +290,10 @@ void WebPUtilBlendPixels(const WebPPicture* const src, void WebPUtilReduceTransparency(const WebPPicture* const src, const WebPFrameRect* const rect, WebPPicture* const dst) { - int j; + int i, j; + assert(src != NULL && dst != NULL && rect != NULL); assert(src->width == dst->width && src->height == dst->height); for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) { - int i; for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) { const uint32_t src_pixel = src->argb[j * src->argb_stride + i]; const int src_alpha = src_pixel >> 24; @@ -306,6 +306,56 @@ void WebPUtilReduceTransparency(const WebPPicture* const src, } } +void WebPUtilFlattenSimilarBlocks(const WebPPicture* const src, + const WebPFrameRect* const rect, + WebPPicture* const dst) { + int i, j; + const int block_size = 8; + const int y_start = (rect->y_offset + block_size) & ~(block_size - 1); + const int y_end = (rect->y_offset + rect->height) & ~(block_size - 1); + const int x_start = (rect->x_offset + block_size) & ~(block_size - 1); + const int x_end = (rect->x_offset + rect->width) & ~(block_size - 1); + assert(src != NULL && dst != NULL && rect != NULL); + assert(src->width == dst->width && src->height == dst->height); + assert((block_size & (block_size - 1)) == 0); // must be a power of 2 + // Iterate over each block and count similar pixels. + for (j = y_start; j < y_end; j += block_size) { + for (i = x_start; i < x_end; i += block_size) { + int cnt = 0; + int avg_r = 0, avg_g = 0, avg_b = 0; + int x, y; + const uint32_t* const psrc = src->argb + j * src->argb_stride + i; + uint32_t* const pdst = dst->argb + j * dst->argb_stride + i; + for (y = 0; y < block_size; ++y) { + for (x = 0; x < block_size; ++x) { + const uint32_t src_pixel = psrc[x + y * src->argb_stride]; + const int alpha = src_pixel >> 24; + if (alpha == 0xff && + src_pixel == pdst[x + y * dst->argb_stride]) { + ++cnt; + avg_r += (src_pixel >> 16) & 0xff; + avg_g += (src_pixel >> 8) & 0xff; + avg_b += (src_pixel >> 0) & 0xff; + } + } + } + // If we have a fully similar block, we replace it with an + // average transparent block. This compresses better in lossy mode. + if (cnt == block_size * block_size) { + const uint32_t color = (0x00 << 24) | + ((avg_r / cnt) << 16) | + ((avg_g / cnt) << 8) | + ((avg_b / cnt) << 0); + for (y = 0; y < block_size; ++y) { + for (x = 0; x < block_size; ++x) { + pdst[x + y * dst->argb_stride] = color; + } + } + } + } + } +} + //------------------------------------------------------------------------------ // Key frame related utilities. diff --git a/examples/gif2webp_util.h b/examples/gif2webp_util.h index a5e2a599..03ef87de 100644 --- a/examples/gif2webp_util.h +++ b/examples/gif2webp_util.h @@ -91,6 +91,12 @@ void WebPUtilReduceTransparency(const struct WebPPicture* const src, const WebPFrameRect* const dst_rect, struct WebPPicture* const dst); +// Replace similar blocks of pixels by a 'see-through' transparent block +// with uniform average color. +void WebPUtilFlattenSimilarBlocks(const WebPPicture* const src, + const WebPFrameRect* const rect, + WebPPicture* const dst); + //------------------------------------------------------------------------------ // Key frame related.