Add utils to better quantize grayscale values to three bit indices while
authorpavel <pavel@cs.unc.edu>
Thu, 23 Oct 2014 20:18:50 +0000 (13:18 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 23 Oct 2014 20:18:50 +0000 (13:18 -0700)
compressing coverage masks.

Signed-off-by: Pavel Krajcevski <pavel@cs.unc.edu>
BUG=skia:

Review URL: https://codereview.chromium.org/669243003

AUTHORS
gyp/utils.gypi
src/utils/SkTextureCompressor_LATC.cpp
src/utils/SkTextureCompressor_R11EAC.cpp
src/utils/SkTextureCompressor_Utils.h [new file with mode: 0755]
tests/TextureCompressionTest.cpp

diff --git a/AUTHORS b/AUTHORS
index ed4c657..51d2bf5 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -30,3 +30,4 @@ Skia Buildbots <skia.buildbots@gmail.com>
 Steve Singer <steve@ssinger.info>
 The Chromium Authors <*@chromium.org>
 Thiago Fransosi Farina <thiago.farina@gmail.com>
+Pavel Krajcevski <pavel@cs.unc.edu>
index e62d287..7d40a8b 100644 (file)
@@ -88,6 +88,7 @@
         '<(skia_src_path)/utils/SkRTConf.cpp',
         '<(skia_src_path)/utils/SkTextureCompressor.cpp',
         '<(skia_src_path)/utils/SkTextureCompressor.h',
+        '<(skia_src_path)/utils/SkTextureCompressor_Utils.h',
         '<(skia_src_path)/utils/SkTextureCompressor_ASTC.cpp',
         '<(skia_src_path)/utils/SkTextureCompressor_ASTC.h',
         '<(skia_src_path)/utils/SkTextureCompressor_Blitter.h',
index 1e5e142..937aec8 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "SkTextureCompressor_LATC.h"
 #include "SkTextureCompressor_Blitter.h"
+#include "SkTextureCompressor_Utils.h"
 
 #include "SkBlitter.h"
 #include "SkEndian.h"
@@ -329,7 +330,7 @@ static inline uint32_t convert_index(uint32_t x) {
     //
     // This first operation takes the mapping from
     // 0 1 2 3 4 5 6 7  -->  7 6 5 4 3 2 1 0
-    x = 0x07070707 - ((x >> 5) & 0x07070707);
+    x = 0x07070707 - SkTextureCompressor::ConvertToThreeBitIndex(x);
 
     // mask is 1 if index is non-zero
     const uint32_t mask = (x | (x >> 1) | (x >> 2)) & 0x01010101;
index 9996eb9..43226e0 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "SkTextureCompressor.h"
 #include "SkTextureCompressor_Blitter.h"
+#include "SkTextureCompressor_Utils.h"
 
 #include "SkBlitter.h"
 #include "SkEndian.h"
@@ -320,7 +321,7 @@ static bool compress_4x4_a8_to_64bit(uint8_t* dst, const uint8_t* src,
 // Most of the voodoo in this function comes from Hacker's Delight, section 2-18
 static inline uint32_t convert_indices(uint32_t x) {
     // Take the top three bits...
-    x = (x & 0xE0E0E0E0) >> 5;
+    x = SkTextureCompressor::ConvertToThreeBitIndex(x);
 
     // Negate...
     x = ~((0x80808080 - x) ^ 0x7F7F7F7F);
diff --git a/src/utils/SkTextureCompressor_Utils.h b/src/utils/SkTextureCompressor_Utils.h
new file mode 100755 (executable)
index 0000000..9b115a2
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+* Copyright 2014 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef SkTextureCompressorUtils_DEFINED
+#define SkTextureCompressorUtils_DEFINED
+
+namespace SkTextureCompressor {
+
+    // In some compression formats used for grayscale alpha, i.e. coverage masks, three
+    // bit indices are used to represent each pixel. A compression scheme must therefore
+    // quantize the full eight bits of grayscale to three bits. The simplest way to do
+    // this is to take the top three bits of the grayscale value. However, this does not
+    // provide an accurate quantization: 192 will be quantized to 219 instead of 185. In
+    // our compression schemes, we let these three-bit indices represent the full range
+    // of grayscale values, and so when we go from three bits to eight bits, we replicate
+    // the three bits into the lower bits of the eight bit value. Below are two different
+    // techniques that offer a quality versus speed tradeoff in terms of quantization.
+#if 1
+    // Divides each byte in the 32-bit argument by three.
+    static inline uint32_t MultibyteDiv3(uint32_t x) {
+        const uint32_t a = (x >> 2) & 0x3F3F3F3F;
+        const uint32_t ar = (x & 0x03030303) << 4;
+
+        const uint32_t b = (x >> 4) & 0x0F0F0F0F;
+        const uint32_t br = (x & 0x0F0F0F0F) << 2;
+
+        const uint32_t c = (x >> 6) & 0x03030303;
+        const uint32_t cr = x & 0x3F3F3F3F;
+
+        return a + b + c + (((ar + br + cr) >> 6) & 0x03030303);
+    }
+
+    // Takes a loaded 32-bit integer of four 8-bit greyscale values and returns their
+    // quantization into 3-bit values, used by LATC and R11 EAC. Instead of taking the
+    // top three bits, the function computes the best three-bit value such that its
+    // reconstruction into an eight bit value via bit replication will yield the best
+    // results. In a 32-bit integer taking the range of values from 0-255 we would add
+    // 18 and divide by 36 (255 / 36 ~= 7). However, since we are working in constrained
+    // 8-bit space, our algorithm is the following:
+    // 1. Shift right by one to give room for overflow
+    // 2. Add 9 (18/2)
+    // 3. Divide by 18 (divide by two, then by three twice)
+    static inline uint32_t ConvertToThreeBitIndex(uint32_t x) {
+        x = (x >> 1) & 0x7F7F7F7F; // 1
+        x = x + 0x09090909;        // 2
+
+        // Need to divide by 18... so first divide by two
+        x = (x >> 1) & 0x7F7F7F7F;
+
+        // Now divide by three twice
+        x = MultibyteDiv3(x);
+        x = MultibyteDiv3(x);
+        return x;
+    }
+#else
+    // Moves the top three bits of each byte in the 32-bit argument to the least
+    // significant bits of their respective byte.
+    static inline uint32_t ConvertToThreeBitIndex(uint32_t x) {
+        return (x >> 5) & 0x07070707;
+    }
+#endif
+}
+
+#endif // SkTextureCompressorUtils_DEFINED
index 7dd285d..568d4d1 100644 (file)
@@ -255,7 +255,39 @@ DEF_TEST(CompressLATC, reporter) {
         // and that the three bits saved per pixel are computed from the top three
         // bits of the luminance value.
         const uint64_t kIndexEncodingMap[8] = { 1, 7, 6, 5, 4, 3, 2, 0 };
-        const uint64_t kIndex = kIndexEncodingMap[lum >> 5];
+
+        // Quantize to three bits in the same way that we do our LATC compression:
+        // 1. Divide by two
+        // 2. Add 9
+        // 3. Divide by two
+        // 4. Approximate division by three twice
+        uint32_t quant = static_cast<uint32_t>(lum);
+        quant >>= 1; // 1
+        quant += 9;  // 2
+        quant >>= 1; // 3
+
+        uint32_t a, b, c, ar, br, cr;
+
+        // First division by three
+        a = quant >> 2;
+        ar = (quant & 0x3) << 4;
+        b = quant >> 4;
+        br = (quant & 0xF) << 2;
+        c = quant >> 6;
+        cr = (quant & 0x3F);
+        quant = (a + b + c) + ((ar + br + cr) >> 6);
+
+        // Second division by three
+        a = quant >> 2;
+        ar = (quant & 0x3) << 4;
+        b = quant >> 4;
+        br = (quant & 0xF) << 2;
+        c = quant >> 6;
+        cr = (quant & 0x3F);
+        quant = (a + b + c) + ((ar + br + cr) >> 6);
+
+        const uint64_t kIndex = kIndexEncodingMap[quant];
+
         const uint64_t kConstColorEncoding =
             SkEndian_SwapLE64(
                 255 |