From 2e3e8b2ef5d2e7d620b5414f8ac2236fd4c3c8dd Mon Sep 17 00:00:00 2001
From: Pascal Massimino <pascal.massimino@gmail.com>
Date: Tue, 17 Jan 2012 08:18:22 +0000
Subject: [PATCH] add a WebPCleanupTransparentArea() method

to 'clean up' the fully-transparent area and make it more compressible
new cwebp flags: -alpha_cleanup (off by default, since gain is not 100% guaranteed)

Change-Id: I74d77e1915eee146584cd61c9c1132a41db922eb
---
 README            | 15 +++++------
 examples/cwebp.c  | 12 +++++++++
 man/cwebp.1       |  4 +++
 src/enc/picture.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
 src/webp/encode.h |  5 ++++
 5 files changed, 93 insertions(+), 8 deletions(-)

diff --git a/README b/README
index 828e0144..11676ff9 100644
--- a/README
+++ b/README
@@ -121,19 +121,21 @@ A longer list of options is available using the -longhelp command line flag:
 Usage:
  cwebp [-preset <...>] [options] in_file [-o out_file]
 
-If input size (-s) for an image is not specified, it is assumed to be a either
-  PNG or JPEG format file.
+If input size (-s) for an image is not specified, it is assumed to be a
+  PNG or JPEG file.
 options:
   -h / -help  ............ short help
   -H / -longhelp  ........ long help
   -q <float> ............. quality factor (0:small..100:big)
-  -alpha_q <int> ......... Transparency-compression quality (0..100)
+  -alpha_q <int> ......... Transparency-compression quality (0..100).
   -preset <string> ....... Preset setting, one of:
                             default, photo, picture,
                             drawing, icon, text
      -preset must come first, as it overwrites other parameters.
   -m <int> ............... compression method (0=fast, 6=slowest)
   -segments <int> ........ number of segments to use (1..4)
+  -size <int> ............ Target size (in bytes)
+  -psnr <float> .......... Target PSNR (in dB. typically: 42)
 
   -s <int> <int> ......... Input size (width x height) for YUV
   -sns <int> ............. Spatial Noise Shaping (0:off, 100:max)
@@ -143,7 +145,6 @@ options:
   -partition_limit <int> . limit quality to fit the 512k limit on
                            the first partition (0=no degradation ... 100=full)
   -pass <int> ............ analysis pass number (1..10)
-  -partitions <int> ...... number of partitions to use (0..3)
   -crop <x> <y> <w> <h> .. crop picture with the given rectangle
   -resize <w> <h> ........ resize picture (after any cropping)
   -map <int> ............. print map of extra info.
@@ -151,6 +152,7 @@ options:
   -alpha_method <int> .... Transparency-compression method (0..1)
   -alpha_filter <string> . predictive filtering for alpha plane.
                            One of: none, fast (default) or best.
+  -alpha_cleanup ......... Clean RGB values in transparent area.
   -noalpha ............... discard any transparency information.
 
   -short ................. condense printed message
@@ -160,11 +162,8 @@ options:
   -v ..................... verbose, e.g. print encoding/decoding times
   -progress .............. report encoding progress
 
-
 Experimental Options:
-  -size <int> ............ Target size (in bytes)
-  -psnr <float> .......... Target PSNR (in dB. typically: 42)
-  -af <int> .............. adjust filter strength (0=off, 1=on)
+  -af .................... auto-adjust filter strength.
   -pre <int> ............. pre-processing filter
 
 
diff --git a/examples/cwebp.c b/examples/cwebp.c
index ebe52afe..d9067dc0 100644
--- a/examples/cwebp.c
+++ b/examples/cwebp.c
@@ -206,6 +206,11 @@ static HRESULT ReadPictureWithWIC(const char* filename,
     if (!ok)
       hr = E_FAIL;
   }
+  if (SUCCEEDED(hr)) {
+    if (has_alpha && keep_alpha == 2) {
+      WebPCleanupTransparentArea(pic);
+    }
+  }
 
   // Cleanup.
   if (pConverter != NULL) IUnknown_Release(pConverter);
@@ -416,6 +421,10 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
                  : WebPPictureImportRGB(pic, rgb, stride);
   free(rgb);
 
+  if (ok && has_alpha && keep_alpha == 2) {
+    WebPCleanupTransparentArea(pic);
+  }
+
  End:
   return ok;
 }
@@ -702,6 +711,7 @@ static void HelpLong(void) {
   printf("  -alpha_method <int> .... Transparency-compression method (0..1)\n");
   printf("  -alpha_filter <string> . predictive filtering for alpha plane.\n");
   printf("                           One of: none, fast (default) or best.\n");
+  printf("  -alpha_cleanup ......... Clean RGB values in transparent area.\n");
   printf("  -noalpha ............... discard any transparency information.\n");
 
   printf("\n");
@@ -795,6 +805,8 @@ int main(int argc, const char *argv[]) {
       config.alpha_quality = strtol(argv[++c], NULL, 0);
     } else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
       config.alpha_compression = strtol(argv[++c], NULL, 0);
+    } else if (!strcmp(argv[c], "-alpha_cleanup")) {
+      keep_alpha = keep_alpha ? 2 : 0;
     } else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
       ++c;
       if (!strcmp(argv[c], "none")) {
diff --git a/man/cwebp.1 b/man/cwebp.1
index 3b306923..2192fd5f 100644
--- a/man/cwebp.1
+++ b/man/cwebp.1
@@ -155,6 +155,10 @@ Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
 no compression, 1 uses uses backward reference counts encoded with arithmetic
 encoder. The default is 1.
 .TP
+.B \-alpha_cleanup
+Modify unseen RGB values under fully transparent area, to help compressibility.
+The default is off.
+.TP
 .B \-noalpha
 Using this option will discard the alpha channel.
 .TP
diff --git a/src/enc/picture.c b/src/enc/picture.c
index 700b88ec..a4312fb9 100644
--- a/src/enc/picture.c
+++ b/src/enc/picture.c
@@ -593,6 +593,71 @@ int WebPPictureImportBGRA(WebPPicture* const picture,
   return Import(picture, rgba, rgba_stride, 4, 1, 1);
 }
 
+//------------------------------------------------------------------------------
+// Helper: clean up fully transparent area to help compressibility.
+
+#define SIZE 8
+#define SIZE2 (SIZE / 2)
+static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
+  int y, x;
+  for (y = 0; y < size; ++y) {
+    for (x = 0; x < size; ++x) {
+      if (ptr[x]) {
+        return 0;
+      }
+    }
+    ptr += stride;
+  }
+  return 1;
+}
+
+static WEBP_INLINE void flatten(uint8_t* ptr, int v, int stride, int size) {
+  int y;
+  for (y = 0; y < size; ++y) {
+    memset(ptr, v, size);
+    ptr += stride;
+  }
+}
+
+void WebPCleanupTransparentArea(WebPPicture* const pic) {
+  int x, y, w, h;
+  const uint8_t* a_ptr;
+  int values[3] = { 0 };
+
+  if (pic == NULL) return;
+
+  a_ptr = pic->a;
+  if (a_ptr == NULL) return;    // nothing to do
+
+  w = pic->width / SIZE;
+  h = pic->height / SIZE;
+  for (y = 0; y < h; ++y) {
+    int need_reset = 1;
+    for (x = 0; x < w; ++x) {
+      const int off_a = (y * pic->a_stride + x) * SIZE;
+      const int off_y = (y * pic->y_stride + x) * SIZE;
+      const int off_uv = (y * pic->uv_stride + x) * SIZE2;
+      if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
+        if (need_reset) {
+          values[0] = pic->y[off_y];
+          values[1] = pic->u[off_uv];
+          values[2] = pic->v[off_uv];
+          need_reset = 0;
+        }
+        flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
+        flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
+        flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
+      } else {
+        need_reset = 1;
+      }
+    }
+    // ignore the left-overs on right/bottom
+  }
+}
+
+#undef SIZE
+#undef SIZE2
+
 //------------------------------------------------------------------------------
 // Simplest call:
 
diff --git a/src/webp/encode.h b/src/webp/encode.h
index 8f6c85b6..8dcac2f9 100644
--- a/src/webp/encode.h
+++ b/src/webp/encode.h
@@ -276,6 +276,11 @@ WEBP_EXTERN(int) WebPPictureImportBGR(
 WEBP_EXTERN(int) WebPPictureImportBGRA(
     WebPPicture* const picture, const uint8_t* const bgra, int bgra_stride);
 
+// Helper function: given a width x height plane of YUV(A) samples
+// (with stride 'stride'), clean-up the YUV samples under fully transparent
+// area, to help compressibility (no guarantee, though).
+WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* const picture);
+
 //------------------------------------------------------------------------------
 // Main call