Add utils to better quantize grayscale values to three bit indices while
compressing coverage masks.
Signed-off-by: Pavel Krajcevski <pavel@cs.unc.edu>
BUG=skia:
Review URL: https://codereview.chromium.org/669243003
diff --git a/AUTHORS b/AUTHORS
index ed4c657..51d2bf5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -30,3 +30,4 @@
Steve Singer <steve@ssinger.info>
The Chromium Authors <*@chromium.org>
Thiago Fransosi Farina <thiago.farina@gmail.com>
+Pavel Krajcevski <pavel@cs.unc.edu>
diff --git a/gyp/utils.gypi b/gyp/utils.gypi
index e62d287..7d40a8b 100644
--- a/gyp/utils.gypi
+++ b/gyp/utils.gypi
@@ -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',
diff --git a/src/utils/SkTextureCompressor_LATC.cpp b/src/utils/SkTextureCompressor_LATC.cpp
index 1e5e142..937aec8 100644
--- a/src/utils/SkTextureCompressor_LATC.cpp
+++ b/src/utils/SkTextureCompressor_LATC.cpp
@@ -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 @@
//
// 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;
diff --git a/src/utils/SkTextureCompressor_R11EAC.cpp b/src/utils/SkTextureCompressor_R11EAC.cpp
index 9996eb9..43226e0 100644
--- a/src/utils/SkTextureCompressor_R11EAC.cpp
+++ b/src/utils/SkTextureCompressor_R11EAC.cpp
@@ -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 @@
// 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
index 0000000..9b115a2
--- /dev/null
+++ b/src/utils/SkTextureCompressor_Utils.h
@@ -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
diff --git a/tests/TextureCompressionTest.cpp b/tests/TextureCompressionTest.cpp
index 7dd285d..568d4d1 100644
--- a/tests/TextureCompressionTest.cpp
+++ b/tests/TextureCompressionTest.cpp
@@ -255,7 +255,39 @@
// 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 |