mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 18:35:41 +01:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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. | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user