From 47eedcc848e7712aea9607fd489abdc70bc8bcfd Mon Sep 17 00:00:00 2001 From: pavel Date: Thu, 23 Oct 2014 13:18:50 -0700 Subject: Add utils to better quantize grayscale values to three bit indices while compressing coverage masks. Signed-off-by: Pavel Krajcevski BUG=skia: Review URL: https://codereview.chromium.org/669243003 --- AUTHORS | 1 + gyp/utils.gypi | 1 + src/utils/SkTextureCompressor_LATC.cpp | 3 +- src/utils/SkTextureCompressor_R11EAC.cpp | 3 +- src/utils/SkTextureCompressor_Utils.h | 68 ++++++++++++++++++++++++++++++++ tests/TextureCompressionTest.cpp | 34 +++++++++++++++- 6 files changed, 107 insertions(+), 3 deletions(-) create mode 100755 src/utils/SkTextureCompressor_Utils.h diff --git a/AUTHORS b/AUTHORS index ed4c657e02..51d2bf5a14 100644 --- a/AUTHORS +++ b/AUTHORS @@ -30,3 +30,4 @@ Skia Buildbots Steve Singer The Chromium Authors <*@chromium.org> Thiago Fransosi Farina +Pavel Krajcevski diff --git a/gyp/utils.gypi b/gyp/utils.gypi index e62d287bb9..7d40a8bf30 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 1e5e142ddb..937aec809e 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 @@ 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; diff --git a/src/utils/SkTextureCompressor_R11EAC.cpp b/src/utils/SkTextureCompressor_R11EAC.cpp index 9996eb9596..43226e0c99 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 @@ 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 index 0000000000..9b115a2964 --- /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 7dd285d4f3..568d4d14b6 100644 --- a/tests/TextureCompressionTest.cpp +++ b/tests/TextureCompressionTest.cpp @@ -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(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 | -- cgit v1.2.3