aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2017-02-10 11:47:58 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-02-10 17:36:43 +0000
commit2e83b13122f93a4f88d1c5471e4b4d71b446e223 (patch)
treeee53a0640f28bf23c804a0a731f2d671fb51c511
parent6aa0e1150ecf7eb5462744a32c356a0006d63c9e (diff)
Remove SkTextureCompressor.
This is the ultimate state of what it looks like to remove SkTextureCompressor. This end result will result from the following steps. 1) Remove Skia dep on ktx (done) 2) Move format over to ktx (done) 3) Remove all SkTexture compressor code CQ_INCLUDE_TRYBOTS=skia.primary:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD Change-Id: I3ad7a6abbea006a3034d95662c652d6db90b86ef Reviewed-on: https://skia-review.googlesource.com/8272 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com> Reviewed-by: Leon Scroggins <scroggo@google.com>
-rw-r--r--gn/tests.gni1
-rw-r--r--gn/utils.gni10
-rw-r--r--src/core/SkOpts.cpp4
-rw-r--r--src/core/SkOpts.h6
-rw-r--r--src/core/SkOverdrawCanvas.cpp1
-rw-r--r--src/opts/SkTextureCompressor_opts.h266
-rw-r--r--src/utils/SkTextureCompressor.cpp231
-rw-r--r--src/utils/SkTextureCompressor.h110
-rw-r--r--src/utils/SkTextureCompressor_ASTC.cpp2100
-rw-r--r--src/utils/SkTextureCompressor_ASTC.h27
-rw-r--r--src/utils/SkTextureCompressor_Blitter.h733
-rw-r--r--src/utils/SkTextureCompressor_LATC.cpp518
-rw-r--r--src/utils/SkTextureCompressor_LATC.h26
-rw-r--r--src/utils/SkTextureCompressor_R11EAC.cpp669
-rw-r--r--src/utils/SkTextureCompressor_R11EAC.h26
-rw-r--r--src/utils/SkTextureCompressor_Utils.h70
-rw-r--r--tests/TextureCompressionTest.cpp277
17 files changed, 1 insertions, 5074 deletions
diff --git a/gn/tests.gni b/gn/tests.gni
index baf1910de4..f0c3f0e62c 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -239,7 +239,6 @@ tests_sources = [
"$_tests/TestUtils.cpp",
"$_tests/TextBlobCacheTest.cpp",
"$_tests/TextBlobTest.cpp",
- "$_tests/TextureCompressionTest.cpp",
"$_tests/Time.cpp",
"$_tests/TLSTest.cpp",
"$_tests/TopoSortTest.cpp",
diff --git a/gn/utils.gni b/gn/utils.gni
index 8d860f5b00..bf02b19eaf 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -71,16 +71,6 @@ skia_utils_sources = [
"$_src/utils/SkShadowTessellator.h",
"$_src/utils/SkShadowUtils.cpp",
"$_src/utils/SkTextBox.cpp",
- "$_src/utils/SkTextureCompressor.cpp",
- "$_src/utils/SkTextureCompressor.h",
- "$_src/utils/SkTextureCompressor_Utils.h",
- "$_src/utils/SkTextureCompressor_ASTC.cpp",
- "$_src/utils/SkTextureCompressor_ASTC.h",
- "$_src/utils/SkTextureCompressor_Blitter.h",
- "$_src/utils/SkTextureCompressor_R11EAC.cpp",
- "$_src/utils/SkTextureCompressor_R11EAC.h",
- "$_src/utils/SkTextureCompressor_LATC.cpp",
- "$_src/utils/SkTextureCompressor_LATC.h",
"$_src/utils/SkThreadUtils.h",
"$_src/utils/SkThreadUtils_pthread.cpp",
"$_src/utils/SkThreadUtils_pthread.h",
diff --git a/src/core/SkOpts.cpp b/src/core/SkOpts.cpp
index aa8aa9772f..67bc976f26 100644
--- a/src/core/SkOpts.cpp
+++ b/src/core/SkOpts.cpp
@@ -45,7 +45,6 @@
#include "SkMorphologyImageFilter_opts.h"
#include "SkRasterPipeline_opts.h"
#include "SkSwizzler_opts.h"
-#include "SkTextureCompressor_opts.h"
#include "SkXfermode_opts.h"
namespace SkOpts {
@@ -65,9 +64,6 @@ namespace SkOpts {
DEFINE_DEFAULT( erode_x);
DEFINE_DEFAULT( erode_y);
- DEFINE_DEFAULT(texture_compressor);
- DEFINE_DEFAULT(fill_block_dimensions);
-
DEFINE_DEFAULT(blit_mask_d32_a8);
DEFINE_DEFAULT(blit_row_color32);
diff --git a/src/core/SkOpts.h b/src/core/SkOpts.h
index 6f8279286a..7dd3a6331c 100644
--- a/src/core/SkOpts.h
+++ b/src/core/SkOpts.h
@@ -10,7 +10,6 @@
#include "SkConvolver.h"
#include "SkRasterPipeline.h"
-#include "SkTextureCompressor.h"
#include "SkTypes.h"
#include "SkXfermodePriv.h"
#include <functional>
@@ -34,11 +33,6 @@ namespace SkOpts {
typedef void (*Morph)(const SkPMColor*, SkPMColor*, int, int, int, int, int);
extern Morph dilate_x, dilate_y, erode_x, erode_y;
- typedef bool (*TextureCompressor)(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes);
- extern TextureCompressor (*texture_compressor)(SkColorType, SkTextureCompressor::Format);
- extern bool (*fill_block_dimensions)(SkTextureCompressor::Format, int* x, int* y);
-
extern void (*blit_mask_d32_a8)(SkPMColor*, size_t, const SkAlpha*, size_t, SkColor, int, int);
extern void (*blit_row_color32)(SkPMColor*, const SkPMColor*, int, SkPMColor);
extern void (*blit_row_s32a_opaque)(SkPMColor*, const SkPMColor*, int, U8CPU);
diff --git a/src/core/SkOverdrawCanvas.cpp b/src/core/SkOverdrawCanvas.cpp
index b51479600a..c6b7b9c9cf 100644
--- a/src/core/SkOverdrawCanvas.cpp
+++ b/src/core/SkOverdrawCanvas.cpp
@@ -8,6 +8,7 @@
#include "SkColorFilter.h"
#include "SkDrawable.h"
#include "SkFindAndPlaceGlyph.h"
+#include "SkImagePriv.h"
#include "SkLatticeIter.h"
#include "SkOverdrawCanvas.h"
#include "SkPatchUtils.h"
diff --git a/src/opts/SkTextureCompressor_opts.h b/src/opts/SkTextureCompressor_opts.h
deleted file mode 100644
index 06ced38bdf..0000000000
--- a/src/opts/SkTextureCompressor_opts.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkTextureCompressor_opts_DEFINED
-#define SkTextureCompressor_opts_DEFINED
-
-#include "SkOpts.h"
-
-namespace SK_OPTS_NS {
-
-#if defined(SK_ARM_HAS_NEON)
- // Converts indices in each of the four bits of the register from
- // 0, 1, 2, 3, 4, 5, 6, 7
- // to
- // 3, 2, 1, 0, 4, 5, 6, 7
- //
- // A more detailed explanation can be found in SkTextureCompressor::convert_indices
- static inline uint8x16_t convert_indices(const uint8x16_t &x) {
- static const int8x16_t kThree = {
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- };
-
- static const int8x16_t kZero = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
-
- // Take top three bits
- int8x16_t sx = vreinterpretq_s8_u8(x);
-
- // Negate ...
- sx = vnegq_s8(sx);
-
- // Add three...
- sx = vaddq_s8(sx, kThree);
-
- // Generate negatives mask
- const int8x16_t mask = vreinterpretq_s8_u8(vcltq_s8(sx, kZero));
-
- // Absolute value
- sx = vabsq_s8(sx);
-
- // Add three to the values that were negative...
- return vreinterpretq_u8_s8(vaddq_s8(sx, vandq_s8(mask, kThree)));
- }
-
- template<unsigned shift>
- static inline uint64x2_t shift_swap(const uint64x2_t &x, const uint64x2_t &mask) {
- uint64x2_t t = vandq_u64(mask, veorq_u64(x, vshrq_n_u64(x, shift)));
- return veorq_u64(x, veorq_u64(t, vshlq_n_u64(t, shift)));
- }
-
- static inline uint64x2_t pack_indices(const uint64x2_t &x) {
- // x: 00 a e 00 b f 00 c g 00 d h 00 i m 00 j n 00 k o 00 l p
-
- static const uint64x2_t kMask1 = { 0x3FC0003FC00000ULL, 0x3FC0003FC00000ULL };
- uint64x2_t ret = shift_swap<10>(x, kMask1);
-
- // x: b f 00 00 00 a e c g i m 00 00 00 d h j n 00 k o 00 l p
- static const uint64x2_t kMask2 = { (0x3FULL << 52), (0x3FULL << 52) };
- static const uint64x2_t kMask3 = { (0x3FULL << 28), (0x3FULL << 28) };
- const uint64x2_t x1 = vandq_u64(vshlq_n_u64(ret, 52), kMask2);
- const uint64x2_t x2 = vandq_u64(vshlq_n_u64(ret, 20), kMask3);
- ret = vshrq_n_u64(vorrq_u64(ret, vorrq_u64(x1, x2)), 16);
-
- // x: 00 00 00 00 00 00 00 00 b f l p a e c g i m k o d h j n
-
- static const uint64x2_t kMask4 = { 0xFC0000ULL, 0xFC0000ULL };
- ret = shift_swap<6>(ret, kMask4);
-
- #if defined (SK_CPU_BENDIAN)
- // x: 00 00 00 00 00 00 00 00 b f l p a e i m c g k o d h j n
-
- static const uint64x2_t kMask5 = { 0x3FULL, 0x3FULL };
- ret = shift_swap<36>(ret, kMask5);
-
- // x: 00 00 00 00 00 00 00 00 b f j n a e i m c g k o d h l p
-
- static const uint64x2_t kMask6 = { 0xFFF000000ULL, 0xFFF000000ULL };
- ret = shift_swap<12>(ret, kMask6);
- #else
- // x: 00 00 00 00 00 00 00 00 c g i m d h l p b f j n a e k o
-
- static const uint64x2_t kMask5 = { 0xFC0ULL, 0xFC0ULL };
- ret = shift_swap<36>(ret, kMask5);
-
- // x: 00 00 00 00 00 00 00 00 a e i m d h l p b f j n c g k o
-
- static const uint64x2_t kMask6 = { (0xFFFULL << 36), (0xFFFULL << 36) };
- static const uint64x2_t kMask7 = { 0xFFFFFFULL, 0xFFFFFFULL };
- static const uint64x2_t kMask8 = { 0xFFFULL, 0xFFFULL };
- const uint64x2_t y1 = vandq_u64(ret, kMask6);
- const uint64x2_t y2 = vshlq_n_u64(vandq_u64(ret, kMask7), 12);
- const uint64x2_t y3 = vandq_u64(vshrq_n_u64(ret, 24), kMask8);
- ret = vorrq_u64(y1, vorrq_u64(y2, y3));
- #endif
-
- // x: 00 00 00 00 00 00 00 00 a e i m b f j n c g k o d h l p
-
- // Set the header
- static const uint64x2_t kHeader = { 0x8490000000000000ULL, 0x8490000000000000ULL };
- return vorrq_u64(kHeader, ret);
- }
-
- // Takes a row of alpha values and places the most significant three bits of each byte into
- // the least significant bits of the same byte
- static inline uint8x16_t make_index_row(const uint8x16_t &x) {
- static const uint8x16_t kTopThreeMask = {
- 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
- 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
- };
- return vshrq_n_u8(vandq_u8(x, kTopThreeMask), 5);
- }
-
- // Returns true if all of the bits in x are 0.
- static inline bool is_zero(uint8x16_t x) {
- // First experiments say that this is way slower than just examining the lanes
- // but it might need a little more investigation.
- #if 0
- // This code path tests the system register for overflow. We trigger
- // overflow by adding x to a register with all of its bits set. The
- // first instruction sets the bits.
- int reg;
- asm ("VTST.8 %%q0, %q1, %q1\n"
- "VQADD.u8 %q1, %%q0\n"
- "VMRS %0, FPSCR\n"
- : "=r"(reg) : "w"(vreinterpretq_f32_u8(x)) : "q0", "q1");
-
- // Bit 21 corresponds to the overflow flag.
- return reg & (0x1 << 21);
- #else
- const uint64x2_t cvt = vreinterpretq_u64_u8(x);
- const uint64_t l1 = vgetq_lane_u64(cvt, 0);
- return (l1 == 0) && (l1 == vgetq_lane_u64(cvt, 1));
- #endif
- }
-
- #if defined (SK_CPU_BENDIAN)
- static inline uint64x2_t fix_endianness(uint64x2_t x) {
- return x;
- }
- #else
- static inline uint64x2_t fix_endianness(uint64x2_t x) {
- return vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(x)));
- }
- #endif
-
- static void compress_r11eac_blocks(uint8_t* dst, const uint8_t* src, size_t rowBytes) {
- // Try to avoid switching between vector and non-vector ops...
- const uint8_t *const src1 = src;
- const uint8_t *const src2 = src + rowBytes;
- const uint8_t *const src3 = src + 2*rowBytes;
- const uint8_t *const src4 = src + 3*rowBytes;
- uint8_t *const dst1 = dst;
- uint8_t *const dst2 = dst + 16;
-
- const uint8x16_t alphaRow1 = vld1q_u8(src1);
- const uint8x16_t alphaRow2 = vld1q_u8(src2);
- const uint8x16_t alphaRow3 = vld1q_u8(src3);
- const uint8x16_t alphaRow4 = vld1q_u8(src4);
-
- const uint8x16_t cmp12 = vceqq_u8(alphaRow1, alphaRow2);
- const uint8x16_t cmp34 = vceqq_u8(alphaRow3, alphaRow4);
- const uint8x16_t cmp13 = vceqq_u8(alphaRow1, alphaRow3);
-
- const uint8x16_t cmp = vandq_u8(vandq_u8(cmp12, cmp34), cmp13);
- const uint8x16_t ncmp = vmvnq_u8(cmp);
- const uint8x16_t nAlphaRow1 = vmvnq_u8(alphaRow1);
- if (is_zero(ncmp)) {
- if (is_zero(alphaRow1)) {
- static const uint64x2_t kTransparent = { 0x0020000000002000ULL,
- 0x0020000000002000ULL };
- vst1q_u8(dst1, vreinterpretq_u8_u64(kTransparent));
- vst1q_u8(dst2, vreinterpretq_u8_u64(kTransparent));
- return;
- } else if (is_zero(nAlphaRow1)) {
- vst1q_u8(dst1, cmp);
- vst1q_u8(dst2, cmp);
- return;
- }
- }
-
- const uint8x16_t indexRow1 = convert_indices(make_index_row(alphaRow1));
- const uint8x16_t indexRow2 = convert_indices(make_index_row(alphaRow2));
- const uint8x16_t indexRow3 = convert_indices(make_index_row(alphaRow3));
- const uint8x16_t indexRow4 = convert_indices(make_index_row(alphaRow4));
-
- const uint64x2_t indexRow12 = vreinterpretq_u64_u8(
- vorrq_u8(vshlq_n_u8(indexRow1, 3), indexRow2));
- const uint64x2_t indexRow34 = vreinterpretq_u64_u8(
- vorrq_u8(vshlq_n_u8(indexRow3, 3), indexRow4));
-
- const uint32x4x2_t blockIndices = vtrnq_u32(vreinterpretq_u32_u64(indexRow12),
- vreinterpretq_u32_u64(indexRow34));
- const uint64x2_t blockIndicesLeft = vreinterpretq_u64_u32(vrev64q_u32(blockIndices.val[0]));
- const uint64x2_t blockIndicesRight = vreinterpretq_u64_u32(vrev64q_u32(blockIndices.val[1]));
-
- const uint64x2_t indicesLeft = fix_endianness(pack_indices(blockIndicesLeft));
- const uint64x2_t indicesRight = fix_endianness(pack_indices(blockIndicesRight));
-
- const uint64x2_t d1 = vcombine_u64(vget_low_u64(indicesLeft), vget_low_u64(indicesRight));
- const uint64x2_t d2 = vcombine_u64(vget_high_u64(indicesLeft), vget_high_u64(indicesRight));
- vst1q_u8(dst1, vreinterpretq_u8_u64(d1));
- vst1q_u8(dst2, vreinterpretq_u8_u64(d2));
- }
-
- static bool compress_a8_r11eac(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes) {
-
- // Since we're going to operate on 4 blocks at a time, the src width
- // must be a multiple of 16. However, the height only needs to be a
- // multiple of 4
- if (0 == width || 0 == height || (width % 16) != 0 || (height % 4) != 0) {
- return false;
- }
-
- const int blocksX = width >> 2;
- const int blocksY = height >> 2;
-
- SkASSERT((blocksX % 4) == 0);
-
- for (int y = 0; y < blocksY; ++y) {
- for (int x = 0; x < blocksX; x+=4) {
- // Compress it
- compress_r11eac_blocks(dst, src + 4*x, rowBytes);
- dst += 32;
- }
- src += 4 * rowBytes;
- }
- return true;
- }
-
- static SkOpts::TextureCompressor texture_compressor(SkColorType ct,
- SkTextureCompressor::Format fmt) {
- if (ct == kAlpha_8_SkColorType && fmt == SkTextureCompressor::kR11_EAC_Format) {
- return compress_a8_r11eac;
- }
- return nullptr;
- }
- static bool fill_block_dimensions(SkTextureCompressor::Format fmt, int* x, int* y) {
- if (fmt == SkTextureCompressor::kR11_EAC_Format) {
- *x = 16;
- *y = 4;
- return true;
- }
- return false;
- }
-
-#else
- static SkOpts::TextureCompressor texture_compressor(SkColorType, SkTextureCompressor::Format) {
- return nullptr;
- }
- static bool fill_block_dimensions(SkTextureCompressor::Format, int*, int*) {
- return false;
- }
-
-#endif
-
-} // namespace SK_OPTS_NS
-
-#endif//SkTextureCompressor_opts_DEFINED
diff --git a/src/utils/SkTextureCompressor.cpp b/src/utils/SkTextureCompressor.cpp
deleted file mode 100644
index e9e4d2e826..0000000000
--- a/src/utils/SkTextureCompressor.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTextureCompressor.h"
-#include "SkTextureCompressor_ASTC.h"
-#include "SkTextureCompressor_LATC.h"
-#include "SkTextureCompressor_R11EAC.h"
-
-#include "SkBitmap.h"
-#include "SkBitmapProcShader.h"
-#include "SkData.h"
-#include "SkEndian.h"
-#include "SkMathPriv.h"
-#include "SkOpts.h"
-
-#ifndef SK_IGNORE_ETC1_SUPPORT
-# include "etc1.h"
-#endif
-
-// Convert ETC1 functions to our function signatures
-static bool compress_etc1_565(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes) {
-#ifndef SK_IGNORE_ETC1_SUPPORT
- return 0 == etc1_encode_image(src, width, height, 2, SkToInt(rowBytes), dst);
-#else
- return false;
-#endif
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace SkTextureCompressor {
-
-void GetBlockDimensions(Format format, int* dimX, int* dimY, bool matchSpec) {
- if (nullptr == dimX || nullptr == dimY) {
- return;
- }
-
- if (!matchSpec && SkOpts::fill_block_dimensions(format, dimX, dimY)) {
- return;
- }
-
- // No specialized arguments, return the dimensions as they are in the spec.
- static const struct FormatDimensions {
- const int fBlockSizeX;
- const int fBlockSizeY;
- } kFormatDimensions[kFormatCnt] = {
- { 4, 4 }, // kLATC_Format
- { 4, 4 }, // kR11_EAC_Format
- { 4, 4 }, // kETC1_Format
- { 4, 4 }, // kASTC_4x4_Format
- { 5, 4 }, // kASTC_5x4_Format
- { 5, 5 }, // kASTC_5x5_Format
- { 6, 5 }, // kASTC_6x5_Format
- { 6, 6 }, // kASTC_6x6_Format
- { 8, 5 }, // kASTC_8x5_Format
- { 8, 6 }, // kASTC_8x6_Format
- { 8, 8 }, // kASTC_8x8_Format
- { 10, 5 }, // kASTC_10x5_Format
- { 10, 6 }, // kASTC_10x6_Format
- { 10, 8 }, // kASTC_10x8_Format
- { 10, 10 }, // kASTC_10x10_Format
- { 12, 10 }, // kASTC_12x10_Format
- { 12, 12 }, // kASTC_12x12_Format
- };
-
- *dimX = kFormatDimensions[format].fBlockSizeX;
- *dimY = kFormatDimensions[format].fBlockSizeY;
-}
-
-int GetCompressedDataSize(Format fmt, int width, int height) {
- int dimX, dimY;
- GetBlockDimensions(fmt, &dimX, &dimY, true);
-
- int encodedBlockSize = 0;
-
- switch (fmt) {
- // These formats are 64 bits per 4x4 block.
- case kLATC_Format:
- case kR11_EAC_Format:
- case kETC1_Format:
- encodedBlockSize = 8;
- break;
-
- // This format is 128 bits.
- case kASTC_4x4_Format:
- case kASTC_5x4_Format:
- case kASTC_5x5_Format:
- case kASTC_6x5_Format:
- case kASTC_6x6_Format:
- case kASTC_8x5_Format:
- case kASTC_8x6_Format:
- case kASTC_8x8_Format:
- case kASTC_10x5_Format:
- case kASTC_10x6_Format:
- case kASTC_10x8_Format:
- case kASTC_10x10_Format:
- case kASTC_12x10_Format:
- case kASTC_12x12_Format:
- encodedBlockSize = 16;
- break;
-
- default:
- SkFAIL("Unknown compressed format!");
- return -1;
- }
-
- if(((width % dimX) == 0) && ((height % dimY) == 0)) {
- const int blocksX = width / dimX;
- const int blocksY = height / dimY;
-
- return blocksX * blocksY * encodedBlockSize;
- }
-
- return -1;
-}
-
-bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType,
- int width, int height, size_t rowBytes, Format format) {
- SkOpts::TextureCompressor proc = SkOpts::texture_compressor(srcColorType, format);
- if (proc && proc(dst, src, width, height, rowBytes)) {
- return true;
- }
-
- switch (srcColorType) {
- case kAlpha_8_SkColorType:
- if (format == kLATC_Format) { proc = CompressA8ToLATC; }
- if (format == kR11_EAC_Format) { proc = CompressA8ToR11EAC; }
- if (format == kASTC_12x12_Format) { proc = CompressA8To12x12ASTC; }
- break;
- case kRGB_565_SkColorType:
- if (format == kETC1_Format) { proc = compress_etc1_565; }
- break;
- default:
- break;
- }
- if (proc && proc(dst, src, width, height, rowBytes)) {
- return true;
- }
-
- return false;
-}
-
-sk_sp<SkData> CompressBitmapToFormat(const SkPixmap& pixmap, Format format) {
- int compressedDataSize = GetCompressedDataSize(format, pixmap.width(), pixmap.height());
- if (compressedDataSize < 0) {
- return nullptr;
- }
-
- const uint8_t* src = reinterpret_cast<const uint8_t*>(pixmap.addr());
- sk_sp<SkData> dst(SkData::MakeUninitialized(compressedDataSize));
-
- if (!CompressBufferToFormat((uint8_t*)dst->writable_data(), src, pixmap.colorType(),
- pixmap.width(), pixmap.height(), pixmap.rowBytes(), format)) {
- return nullptr;
- }
- return dst;
-}
-
-SkBlitter* CreateBlitterForFormat(int width, int height, void* compressedBuffer,
- SkTBlitterAllocator *allocator, Format format) {
- switch(format) {
- case kLATC_Format:
- return CreateLATCBlitter(width, height, compressedBuffer, allocator);
-
- case kR11_EAC_Format:
- return CreateR11EACBlitter(width, height, compressedBuffer, allocator);
-
- case kASTC_12x12_Format:
- return CreateASTCBlitter(width, height, compressedBuffer, allocator);
-
- default:
- return nullptr;
- }
-
- return nullptr;
-}
-
-bool DecompressBufferFromFormat(uint8_t* dst, int dstRowBytes, const uint8_t* src,
- int width, int height, Format format) {
- int dimX, dimY;
- GetBlockDimensions(format, &dimX, &dimY, true);
-
- if (width < 0 || ((width % dimX) != 0) || height < 0 || ((height % dimY) != 0)) {
- return false;
- }
-
- switch(format) {
- case kLATC_Format:
- DecompressLATC(dst, dstRowBytes, src, width, height);
- return true;
-
- case kR11_EAC_Format:
- DecompressR11EAC(dst, dstRowBytes, src, width, height);
- return true;
-
-#ifndef SK_IGNORE_ETC1_SUPPORT
- case kETC1_Format:
- return 0 == etc1_decode_image(src, dst, width, height, 3, dstRowBytes);
-#endif
-
- case kASTC_4x4_Format:
- case kASTC_5x4_Format:
- case kASTC_5x5_Format:
- case kASTC_6x5_Format:
- case kASTC_6x6_Format:
- case kASTC_8x5_Format:
- case kASTC_8x6_Format:
- case kASTC_8x8_Format:
- case kASTC_10x5_Format:
- case kASTC_10x6_Format:
- case kASTC_10x8_Format:
- case kASTC_10x10_Format:
- case kASTC_12x10_Format:
- case kASTC_12x12_Format:
- DecompressASTC(dst, dstRowBytes, src, width, height, dimX, dimY);
- return true;
-
- default:
- // Do nothing...
- break;
- }
-
- return false;
-}
-
-} // namespace SkTextureCompressor
diff --git a/src/utils/SkTextureCompressor.h b/src/utils/SkTextureCompressor.h
deleted file mode 100644
index 1ae4aef132..0000000000
--- a/src/utils/SkTextureCompressor.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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 SkTextureCompressor_DEFINED
-#define SkTextureCompressor_DEFINED
-
-#include "SkBitmapProcShader.h"
-#include "SkImageInfo.h"
-
-class SkBitmap;
-class SkBlitter;
-class SkData;
-
-namespace SkTextureCompressor {
- // Various texture compression formats that we support.
- enum Format {
- // Alpha only formats.
- kLATC_Format, // 4x4 blocks, (de)compresses A8
- kR11_EAC_Format, // 4x4 blocks, (de)compresses A8
-
- // RGB only formats
- kETC1_Format, // 4x4 blocks, compresses RGB 565, decompresses 8-bit RGB
- // NOTE: ETC1 supports 8-bit RGB compression, but we
- // currently don't have any RGB8 SkColorTypes. We could
- // support 8-bit RGBA but we would have to preprocess the
- // bitmap to insert alphas.
-
- // Multi-purpose formats
- kASTC_4x4_Format, // 4x4 blocks, no compression, decompresses RGBA
- kASTC_5x4_Format, // 5x4 blocks, no compression, decompresses RGBA
- kASTC_5x5_Format, // 5x5 blocks, no compression, decompresses RGBA
- kASTC_6x5_Format, // 6x5 blocks, no compression, decompresses RGBA
- kASTC_6x6_Format, // 6x6 blocks, no compression, decompresses RGBA
- kASTC_8x5_Format, // 8x5 blocks, no compression, decompresses RGBA
- kASTC_8x6_Format, // 8x6 blocks, no compression, decompresses RGBA
- kASTC_8x8_Format, // 8x8 blocks, no compression, decompresses RGBA
- kASTC_10x5_Format, // 10x5 blocks, no compression, decompresses RGBA
- kASTC_10x6_Format, // 10x6 blocks, no compression, decompresses RGBA
- kASTC_10x8_Format, // 10x8 blocks, no compression, decompresses RGBA
- kASTC_10x10_Format, // 10x10 blocks, no compression, decompresses RGBA
- kASTC_12x10_Format, // 12x10 blocks, no compression, decompresses RGBA
- kASTC_12x12_Format, // 12x12 blocks, compresses A8, decompresses RGBA
-
- kLast_Format = kASTC_12x12_Format
- };
- static const int kFormatCnt = kLast_Format + 1;
-
- // Returns the size of the compressed data given the width, height, and
- // desired compression format. If the width and height are not an appropriate
- // multiple of the block size, then this function returns an error (-1).
- int GetCompressedDataSize(Format fmt, int width, int height);
-
- // Returns an SkData holding a blob of compressed data that corresponds
- // to the pixmap. If the pixmap colorType cannot be compressed using the
- // associated format, then we return nullptr.
- sk_sp<SkData> CompressBitmapToFormat(const SkPixmap&, Format format);
-
- // Compresses the given src data into dst. The src data is assumed to be
- // large enough to hold width*height pixels. The dst data is expected to
- // be large enough to hold the compressed data according to the format.
- bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType,
- int width, int height, size_t rowBytes, Format format);
-
- // Decompresses the given src data from the format specified into the
- // destination buffer. The width and height of the data passed corresponds
- // to the width and height of the uncompressed image. The destination buffer (dst)
- // is assumed to be large enough to hold the entire decompressed image. The
- // decompressed image colors are determined based on the passed format.
- //
- // Note, CompressBufferToFormat compresses A8 data into ASTC. However,
- // general ASTC data encodes RGBA data, so that is what the decompressor
- // operates on.
- //
- // Returns true if successfully decompresses the src data.
- bool DecompressBufferFromFormat(uint8_t* dst, int dstRowBytes, const uint8_t* src,
- int width, int height, Format format);
-
- // Returns true if there exists a blitter for the specified format.
- inline bool ExistsBlitterForFormat(Format format) {
- switch (format) {
- case kLATC_Format:
- case kR11_EAC_Format:
- case kASTC_12x12_Format:
- return true;
-
- default:
- return false;
- }
- }
-
- // Returns the blitter for the given compression format. Note, the blitter
- // is intended to be used with the proper input. I.e. if you try to blit
- // RGB source data into an R11 EAC texture, you're gonna have a bad time.
- SkBlitter* CreateBlitterForFormat(int width, int height, void* compressedBuffer,
- SkTBlitterAllocator *allocator, Format format);
-
- // Returns the desired dimensions of the block size for the given format. These dimensions
- // don't necessarily correspond to the specification's dimensions, since there may
- // be specialized algorithms that operate on multiple blocks at once. If the
- // flag 'matchSpec' is true, then the actual dimensions from the specification are
- // returned. If the flag is false, then these dimensions reflect the appropriate operable
- // dimensions of the compression functions.
- void GetBlockDimensions(Format format, int* dimX, int* dimY, bool matchSpec = false);
-}
-
-#endif
diff --git a/src/utils/SkTextureCompressor_ASTC.cpp b/src/utils/SkTextureCompressor_ASTC.cpp
deleted file mode 100644
index 49b34de4f5..0000000000
--- a/src/utils/SkTextureCompressor_ASTC.cpp
+++ /dev/null
@@ -1,2100 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTextureCompressor_ASTC.h"
-#include "SkTextureCompressor_Blitter.h"
-
-#include "SkBlitter.h"
-#include "SkEndian.h"
-#include "SkMathPriv.h"
-
-// This table contains the weight values for each texel. This is used in determining
-// how to convert a 12x12 grid of alpha values into a 6x5 grid of index values. Since
-// we have a 6x5 grid, that gives 30 values that we have to compute. For each index,
-// we store up to 20 different triplets of values. In order the triplets are:
-// weight, texel-x, texel-y
-// The weight value corresponds to the amount that this index contributes to the final
-// index value of the given texel. Hence, we need to reconstruct the 6x5 index grid
-// from their relative contribution to the 12x12 texel grid.
-//
-// The algorithm is something like this:
-// foreach index i:
-// total-weight = 0;
-// total-alpha = 0;
-// for w = 1 to 20:
-// weight = table[i][w*3];
-// texel-x = table[i][w*3 + 1];
-// texel-y = table[i][w*3 + 2];
-// if weight >= 0:
-// total-weight += weight;
-// total-alpha += weight * alphas[texel-x][texel-y];
-//
-// total-alpha /= total-weight;
-// index = top three bits of total-alpha
-//
-// If the associated index does not contribute to 20 different texels (e.g. it's in
-// a corner), then the extra texels are stored with -1's in the table.
-
-static const int8_t k6x5To12x12Table[30][60] = {
-{ 16, 0, 0, 9, 1, 0, 1, 2, 0, 10, 0, 1, 6, 1, 1, 1, 2, 1, 4, 0, 2, 2,
- 1, 2, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 7, 1, 0, 15, 2, 0, 10, 3, 0, 3, 4, 0, 4, 1, 1, 9, 2, 1, 6, 3, 1, 2,
- 4, 1, 2, 1, 2, 4, 2, 2, 3, 3, 2, 1, 4, 2, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 6, 3, 0, 13, 4, 0, 12, 5, 0, 4, 6, 0, 4, 3, 1, 8, 4, 1, 8, 5, 1, 3,
- 6, 1, 1, 3, 2, 3, 4, 2, 3, 5, 2, 1, 6, 2, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 4, 5, 0, 12, 6, 0, 13, 7, 0, 6, 8, 0, 2, 5, 1, 7, 6, 1, 8, 7, 1, 4,
- 8, 1, 1, 5, 2, 3, 6, 2, 3, 7, 2, 2, 8, 2, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 3, 7, 0, 10, 8, 0, 15, 9, 0, 7, 10, 0, 2, 7, 1, 6, 8, 1, 9, 9, 1, 4,
- 10, 1, 1, 7, 2, 2, 8, 2, 4, 9, 2, 2, 10, 2, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 9, 0, 9, 10, 0, 16, 11, 0, 1, 9, 1, 6, 10, 1, 10, 11, 1, 2, 10, 2, 4,
- 11, 2, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 6, 0, 1, 3, 1, 1, 12, 0, 2, 7, 1, 2, 1, 2, 2, 15, 0, 3, 8, 1, 3, 1,
- 2, 3, 9, 0, 4, 5, 1, 4, 1, 2, 4, 3, 0, 5, 2, 1, 5, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 3, 1, 1, 6, 2, 1, 4, 3, 1, 1, 4, 1, 5, 1, 2, 11, 2, 2, 7, 3, 2, 2,
- 4, 2, 7, 1, 3, 14, 2, 3, 9, 3, 3, 3, 4, 3, 4, 1, 4, 8, 2, 4, 6, 3,
- 4, 2, 4, 4, 1, 1, 5, 3, 2, 5, 2, 3, 5, 1, 4, 5}, // n = 20
-{ 2, 3, 1, 5, 4, 1, 4, 5, 1, 1, 6, 1, 5, 3, 2, 10, 4, 2, 9, 5, 2, 3,
- 6, 2, 6, 3, 3, 12, 4, 3, 11, 5, 3, 4, 6, 3, 3, 3, 4, 7, 4, 4, 7, 5,
- 4, 2, 6, 4, 1, 3, 5, 2, 4, 5, 2, 5, 5, 1, 6, 5}, // n = 20
-{ 2, 5, 1, 5, 6, 1, 5, 7, 1, 2, 8, 1, 3, 5, 2, 9, 6, 2, 10, 7, 2, 4,
- 8, 2, 4, 5, 3, 11, 6, 3, 12, 7, 3, 6, 8, 3, 2, 5, 4, 7, 6, 4, 7, 7,
- 4, 3, 8, 4, 1, 5, 5, 2, 6, 5, 2, 7, 5, 1, 8, 5}, // n = 20
-{ 1, 7, 1, 4, 8, 1, 6, 9, 1, 3, 10, 1, 2, 7, 2, 8, 8, 2, 11, 9, 2, 5,
- 10, 2, 3, 7, 3, 9, 8, 3, 14, 9, 3, 7, 10, 3, 2, 7, 4, 6, 8, 4, 8, 9,
- 4, 4, 10, 4, 1, 7, 5, 2, 8, 5, 3, 9, 5, 1, 10, 5}, // n = 20
-{ 3, 10, 1, 6, 11, 1, 1, 9, 2, 7, 10, 2, 12, 11, 2, 1, 9, 3, 8, 10, 3, 15,
- 11, 3, 1, 9, 4, 5, 10, 4, 9, 11, 4, 2, 10, 5, 3, 11, 5, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 0, 3, 1, 1, 3, 7, 0, 4, 4, 1, 4, 13, 0, 5, 7, 1, 5, 1, 2, 5, 13,
- 0, 6, 7, 1, 6, 1, 2, 6, 7, 0, 7, 4, 1, 7, 1, 0, 8, 1, 1, 8, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 2, 3, 1, 3, 3, 3, 1, 4, 7, 2, 4, 4, 3, 4, 1, 4, 4, 6, 1, 5, 12,
- 2, 5, 8, 3, 5, 2, 4, 5, 6, 1, 6, 12, 2, 6, 8, 3, 6, 2, 4, 6, 3, 1,
- 7, 7, 2, 7, 4, 3, 7, 1, 4, 7, 1, 2, 8, 1, 3, 8}, // n = 20
-{ 1, 4, 3, 1, 5, 3, 3, 3, 4, 6, 4, 4, 5, 5, 4, 2, 6, 4, 5, 3, 5, 11,
- 4, 5, 10, 5, 5, 3, 6, 5, 5, 3, 6, 11, 4, 6, 10, 5, 6, 3, 6, 6, 3, 3,
- 7, 6, 4, 7, 5, 5, 7, 2, 6, 7, 1, 4, 8, 1, 5, 8}, // n = 20
-{ 1, 6, 3, 1, 7, 3, 2, 5, 4, 5, 6, 4, 6, 7, 4, 3, 8, 4, 3, 5, 5, 10,
- 6, 5, 11, 7, 5, 5, 8, 5, 3, 5, 6, 10, 6, 6, 11, 7, 6, 5, 8, 6, 2, 5,
- 7, 5, 6, 7, 6, 7, 7, 3, 8, 7, 1, 6, 8, 1, 7, 8}, // n = 20
-{ 1, 8, 3, 1, 9, 3, 1, 7, 4, 4, 8, 4, 7, 9, 4, 3, 10, 4, 2, 7, 5, 8,
- 8, 5, 12, 9, 5, 6, 10, 5, 2, 7, 6, 8, 8, 6, 12, 9, 6, 6, 10, 6, 1, 7,
- 7, 4, 8, 7, 7, 9, 7, 3, 10, 7, 1, 8, 8, 1, 9, 8}, // n = 20
-{ 1, 10, 3, 1, 11, 3, 4, 10, 4, 7, 11, 4, 1, 9, 5, 7, 10, 5, 13, 11, 5, 1,
- 9, 6, 7, 10, 6, 13, 11, 6, 4, 10, 7, 7, 11, 7, 1, 10, 8, 1, 11, 8, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 3, 0, 6, 2, 1, 6, 9, 0, 7, 5, 1, 7, 1, 2, 7, 15, 0, 8, 8, 1, 8, 1,
- 2, 8, 12, 0, 9, 7, 1, 9, 1, 2, 9, 6, 0, 10, 3, 1, 10, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 1, 6, 3, 2, 6, 2, 3, 6, 1, 4, 6, 4, 1, 7, 8, 2, 7, 6, 3, 7, 2,
- 4, 7, 7, 1, 8, 14, 2, 8, 9, 3, 8, 3, 4, 8, 5, 1, 9, 11, 2, 9, 8, 3,
- 9, 2, 4, 9, 3, 1, 10, 6, 2, 10, 4, 3, 10, 1, 4, 10}, // n = 20
-{ 1, 3, 6, 2, 4, 6, 2, 5, 6, 1, 6, 6, 3, 3, 7, 7, 4, 7, 7, 5, 7, 2,
- 6, 7, 6, 3, 8, 12, 4, 8, 11, 5, 8, 4, 6, 8, 4, 3, 9, 10, 4, 9, 9, 5,
- 9, 3, 6, 9, 2, 3, 10, 5, 4, 10, 5, 5, 10, 2, 6, 10}, // n = 20
-{ 1, 5, 6, 2, 6, 6, 2, 7, 6, 1, 8, 6, 2, 5, 7, 7, 6, 7, 7, 7, 7, 3,
- 8, 7, 4, 5, 8, 11, 6, 8, 12, 7, 8, 6, 8, 8, 3, 5, 9, 9, 6, 9, 10, 7,
- 9, 5, 8, 9, 1, 5, 10, 4, 6, 10, 5, 7, 10, 2, 8, 10}, // n = 20
-{ 1, 7, 6, 2, 8, 6, 3, 9, 6, 1, 10, 6, 2, 7, 7, 6, 8, 7, 8, 9, 7, 4,
- 10, 7, 3, 7, 8, 9, 8, 8, 14, 9, 8, 7, 10, 8, 2, 7, 9, 7, 8, 9, 11, 9,
- 9, 5, 10, 9, 1, 7, 10, 4, 8, 10, 6, 9, 10, 3, 10, 10}, // n = 20
-{ 2, 10, 6, 3, 11, 6, 1, 9, 7, 5, 10, 7, 9, 11, 7, 1, 9, 8, 8, 10, 8, 15,
- 11, 8, 1, 9, 9, 7, 10, 9, 12, 11, 9, 3, 10, 10, 6, 11, 10, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 4, 0, 9, 2, 1, 9, 10, 0, 10, 6, 1, 10, 1, 2, 10, 16, 0, 11, 9, 1, 11, 1,
- 2, 11, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 2, 1, 9, 4, 2, 9, 2, 3, 9, 1, 4, 9, 4, 1, 10, 9, 2, 10, 6, 3, 10, 2,
- 4, 10, 7, 1, 11, 15, 2, 11, 10, 3, 11, 3, 4, 11, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 2, 3, 9, 3, 4, 9, 3, 5, 9, 1, 6, 9, 4, 3, 10, 8, 4, 10, 7, 5, 10, 2,
- 6, 10, 6, 3, 11, 13, 4, 11, 12, 5, 11, 4, 6, 11, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 5, 9, 3, 6, 9, 3, 7, 9, 1, 8, 9, 3, 5, 10, 8, 6, 10, 8, 7, 10, 4,
- 8, 10, 4, 5, 11, 12, 6, 11, 13, 7, 11, 6, 8, 11, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 1, 7, 9, 3, 8, 9, 4, 9, 9, 2, 10, 9, 2, 7, 10, 6, 8, 10, 9, 9, 10, 4,
- 10, 10, 3, 7, 11, 10, 8, 11, 15, 9, 11, 7, 10, 11, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0}, // n = 20
-{ 2, 10, 9, 4, 11, 9, 1, 9, 10, 6, 10, 10, 10, 11, 10, 1, 9, 11, 9, 10, 11, 16,
- 11, 11, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
- 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0} // n = 20
-};
-
-// Returns the alpha value of a texel at position (x, y) from src.
-// (x, y) are assumed to be in the range [0, 12).
-inline uint8_t GetAlpha(const uint8_t *src, size_t rowBytes, int x, int y) {
- SkASSERT(x >= 0 && x < 12);
- SkASSERT(y >= 0 && y < 12);
- SkASSERT(rowBytes >= 12);
- return *(src + y*rowBytes + x);
-}
-
-inline uint8_t GetAlphaTranspose(const uint8_t *src, size_t rowBytes, int x, int y) {
- return GetAlpha(src, rowBytes, y, x);
-}
-
-// Output the 16 bytes stored in top and bottom and advance the pointer. The bytes
-// are stored as the integers are represented in memory, so they should be swapped
-// if necessary.
-static inline void send_packing(uint8_t** dst, const uint64_t top, const uint64_t bottom) {
- uint64_t* dst64 = reinterpret_cast<uint64_t*>(*dst);
- dst64[0] = top;
- dst64[1] = bottom;
- *dst += 16;
-}
-
-// Compresses an ASTC block, by looking up the proper contributions from
-// k6x5To12x12Table and computing an index from the associated values.
-typedef uint8_t (*GetAlphaProc)(const uint8_t* src, size_t rowBytes, int x, int y);
-
-template<GetAlphaProc getAlphaProc>
-static void compress_a8_astc_block(uint8_t** dst, const uint8_t* src, size_t rowBytes) {
- // Check for single color
- bool constant = true;
- const uint32_t firstInt = *(reinterpret_cast<const uint32_t*>(src));
- for (int i = 0; i < 12; ++i) {
- const uint32_t *rowInt = reinterpret_cast<const uint32_t *>(src + i*rowBytes);
- constant = constant && (rowInt[0] == firstInt);
- constant = constant && (rowInt[1] == firstInt);
- constant = constant && (rowInt[2] == firstInt);
- }
-
- if (constant) {
- if (0 == firstInt) {
- // All of the indices are set to zero, and the colors are
- // v0 = 0, v1 = 255, so everything will be transparent.
- send_packing(dst, SkTEndian_SwapLE64(0x0000000001FE000173ULL), 0);
- return;
- } else if (0xFFFFFFFF == firstInt) {
- // All of the indices are set to zero, and the colors are
- // v0 = 255, v1 = 0, so everything will be opaque.
- send_packing(dst, SkTEndian_SwapLE64(0x000000000001FE0173ULL), 0);
- return;
- }
- }
-
- uint8_t indices[30]; // 6x5 index grid
- for (int idx = 0; idx < 30; ++idx) {
- int weightTot = 0;
- int alphaTot = 0;
- for (int w = 0; w < 20; ++w) {
- const int8_t weight = k6x5To12x12Table[idx][w*3];
- if (weight > 0) {
- const int x = k6x5To12x12Table[idx][w*3 + 1];
- const int y = k6x5To12x12Table[idx][w*3 + 2];
- weightTot += weight;
- alphaTot += weight * getAlphaProc(src, rowBytes, x, y);
- } else {
- // In our table, not every entry has 20 weights, and all
- // of them are nonzero. Once we hit a negative weight, we
- // know that all of the other weights are not valid either.
- break;
- }
- }
-
- indices[idx] = (alphaTot / weightTot) >> 5;
- }
-
- // Pack indices... The ASTC block layout is fairly complicated. An extensive
- // description can be found here:
- // https://www.opengl.org/registry/specs/KHR/texture_compression_astc_hdr.txt
- //
- // Here is a summary of the options that we've chosen:
- // 1. Block mode: 0b00101110011
- // - 6x5 texel grid
- // - Single plane
- // - Low-precision index values
- // - Index range 0-7 (three bits per index)
- // 2. Partitions: 0b00
- // - One partition
- // 3. Color Endpoint Mode: 0b0000
- // - Direct luminance -- e0=(v0,v0,v0,0xFF); e1=(v1,v1,v1,0xFF);
- // 4. 8-bit endpoints:
- // v0 = 0, v1 = 255
- //
- // The rest of the block contains the 30 index values from before, which
- // are currently stored in the indices variable.
-
- uint64_t top = 0x0000000001FE000173ULL;
- uint64_t bottom = 0;
-
- for (int idx = 0; idx <= 20; ++idx) {
- const uint8_t index = indices[idx];
- bottom |= static_cast<uint64_t>(index) << (61-(idx*3));
- }
-
- // index 21 straddles top and bottom
- {
- const uint8_t index = indices[21];
- bottom |= index & 1;
- top |= static_cast<uint64_t>((index >> 2) | (index & 2)) << 62;
- }
-
- for (int idx = 22; idx < 30; ++idx) {
- const uint8_t index = indices[idx];
- top |= static_cast<uint64_t>(index) << (59-(idx-22)*3);
- }
-
- // Reverse each 3-bit index since indices are read in reverse order...
- uint64_t t = (bottom ^ (bottom >> 2)) & 0x2492492492492492ULL;
- bottom = bottom ^ t ^ (t << 2);
-
- t = (top ^ (top >> 2)) & 0x0924924000000000ULL;
- top = top ^ t ^ (t << 2);
-
- send_packing(dst, SkEndian_SwapLE64(top), SkEndian_SwapLE64(bottom));
-}
-
-inline void CompressA8ASTCBlockVertical(uint8_t* dst, const uint8_t* src) {
- compress_a8_astc_block<GetAlphaTranspose>(&dst, src, 12);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// ASTC Decoder
-//
-// Full details available in the spec:
-// http://www.khronos.org/registry/gles/extensions/OES/OES_texture_compression_astc.txt
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Enable this to assert whenever a decoded block has invalid ASTC values. Otherwise,
-// each invalid block will result in a disgusting magenta color.
-#define ASSERT_ASTC_DECODE_ERROR 0
-
-// Reverse 64-bit integer taken from TAOCP 4a, although it's better
-// documented at this site:
-// http://matthewarcus.wordpress.com/2012/11/18/reversing-a-64-bit-word/
-
-template <typename T, T m, int k>
-static inline T swap_bits(T p) {
- T q = ((p>>k)^p) & m;
- return p^q^(q<<k);
-}
-
-static inline uint64_t reverse64(uint64_t n) {
- static const uint64_t m0 = 0x5555555555555555ULL;
- static const uint64_t m1 = 0x0300c0303030c303ULL;
- static const uint64_t m2 = 0x00c0300c03f0003fULL;
- static const uint64_t m3 = 0x00000ffc00003fffULL;
- n = ((n>>1)&m0) | (n&m0)<<1;
- n = swap_bits<uint64_t, m1, 4>(n);
- n = swap_bits<uint64_t, m2, 8>(n);
- n = swap_bits<uint64_t, m3, 20>(n);
- n = (n >> 34) | (n << 30);
- return n;
-}
-
-// An ASTC block is 128 bits. We represent it as two 64-bit integers in order
-// to efficiently operate on the block using bitwise operations.
-struct ASTCBlock {
- uint64_t fLow;
- uint64_t fHigh;
-
- // Reverses the bits of an ASTC block, making the LSB of the
- // 128 bit block the MSB.
- inline void reverse() {
- const uint64_t newLow = reverse64(this->fHigh);
- this->fHigh = reverse64(this->fLow);
- this->fLow = newLow;
- }
-};
-
-// Writes the given color to every pixel in the block. This is used by void-extent
-// blocks (a special constant-color encoding of a block) and by the error function.
-static inline void write_constant_color(uint8_t* dst, int blockDimX, int blockDimY,
- int dstRowBytes, SkColor color) {
- for (int y = 0; y < blockDimY; ++y) {
- SkColor *dstColors = reinterpret_cast<SkColor*>(dst);
- for (int x = 0; x < blockDimX; ++x) {
- dstColors[x] = color;
- }
- dst += dstRowBytes;
- }
-}
-
-// Sets the entire block to the ASTC "error" color, a disgusting magenta
-// that's not supposed to appear in natural images.
-static inline void write_error_color(uint8_t* dst, int blockDimX, int blockDimY,
- int dstRowBytes) {
- static const SkColor kASTCErrorColor = SkColorSetRGB(0xFF, 0, 0xFF);
-
-#if ASSERT_ASTC_DECODE_ERROR
- SkDEBUGFAIL("ASTC decoding error!\n");
-#endif
-
- write_constant_color(dst, blockDimX, blockDimY, dstRowBytes, kASTCErrorColor);
-}
-
-// Reads up to 64 bits of the ASTC block starting from bit
-// 'from' and going up to but not including bit 'to'. 'from' starts
-// counting from the LSB, counting up to the MSB. Returns -1 on
-// error.
-static uint64_t read_astc_bits(const ASTCBlock &block, int from, int to) {
- SkASSERT(0 <= from && from <= 128);
- SkASSERT(0 <= to && to <= 128);
-
- const int nBits = to - from;
- if (0 == nBits) {
- return 0;
- }
-
- if (nBits < 0 || 64 <= nBits) {
- SkDEBUGFAIL("ASTC -- shouldn't read more than 64 bits");
- return -1;
- }
-
- // Remember, the 'to' bit isn't read.
- uint64_t result = 0;
- if (to <= 64) {
- // All desired bits are in the low 64-bits.
- result = (block.fLow >> from) & ((1ULL << nBits) - 1);
- } else if (from >= 64) {
- // All desired bits are in the high 64-bits.
- result = (block.fHigh >> (from - 64)) & ((1ULL << nBits) - 1);
- } else {
- // from < 64 && to > 64
- SkASSERT(nBits > (64 - from));
- const int nLow = 64 - from;
- const int nHigh = nBits - nLow;
- result =
- ((block.fLow >> from) & ((1ULL << nLow) - 1)) |
- ((block.fHigh & ((1ULL << nHigh) - 1)) << nLow);
- }
-
- return result;
-}
-
-// Returns the number of bits needed to represent a number
-// in the given power-of-two range (excluding the power of two itself).
-static inline int bits_for_range(int x) {
- SkASSERT(SkIsPow2(x));
- SkASSERT(0 != x);
- // Since we know it's a power of two, there should only be one bit set,
- // meaning the number of trailing zeros is 31 minus the number of leading
- // zeros.
- return 31 - SkCLZ(x);
-}
-
-// Clamps an integer to the range [0, 255]
-static inline int clamp_byte(int x) {
- return SkClampMax(x, 255);
-}
-
-// Helper function defined in the ASTC spec, section C.2.14
-// It transfers a few bits of precision from one value to another.
-static inline void bit_transfer_signed(int *a, int *b) {
- *b >>= 1;
- *b |= *a & 0x80;
- *a >>= 1;
- *a &= 0x3F;
- if ( (*a & 0x20) != 0 ) {
- *a -= 0x40;
- }
-}
-
-// Helper function defined in the ASTC spec, section C.2.14
-// It uses the value in the blue channel to tint the red and green
-static inline SkColor blue_contract(int a, int r, int g, int b) {
- return SkColorSetARGB(a, (r + b) >> 1, (g + b) >> 1, b);
-}
-
-// Helper function that decodes two colors from eight values. If isRGB is true,
-// then the pointer 'v' contains six values and the last two are considered to be
-// 0xFF. If isRGB is false, then all eight values come from the pointer 'v'. This
-// corresponds to the decode procedure for the following endpoint modes:
-// kLDR_RGB_Direct_ColorEndpointMode
-// kLDR_RGBA_Direct_ColorEndpointMode
-static inline void decode_rgba_direct(const int *v, SkColor *endpoints, bool isRGB) {
-
- int v6 = 0xFF;
- int v7 = 0xFF;
- if (!isRGB) {
- v6 = v[6];
- v7 = v[7];
- }
-
- const int s0 = v[0] + v[2] + v[4];
- const int s1 = v[1] + v[3] + v[5];
-
- if (s1 >= s0) {
- endpoints[0] = SkColorSetARGB(v6, v[0], v[2], v[4]);
- endpoints[1] = SkColorSetARGB(v7, v[1], v[3], v[5]);
- } else {
- endpoints[0] = blue_contract(v7, v[1], v[3], v[5]);
- endpoints[1] = blue_contract(v6, v[0], v[2], v[4]);
- }
-}
-
-// Helper function that decodes two colors from six values. If isRGB is true,
-// then the pointer 'v' contains four values and the last two are considered to be
-// 0xFF. If isRGB is false, then all six values come from the pointer 'v'. This
-// corresponds to the decode procedure for the following endpoint modes:
-// kLDR_RGB_BaseScale_ColorEndpointMode
-// kLDR_RGB_BaseScaleWithAlpha_ColorEndpointMode
-static inline void decode_rgba_basescale(const int *v, SkColor *endpoints, bool isRGB) {
-
- int v4 = 0xFF;
- int v5 = 0xFF;
- if (!isRGB) {
- v4 = v[4];
- v5 = v[5];
- }
-
- endpoints[0] = SkColorSetARGB(v4,
- (v[0]*v[3]) >> 8,
- (v[1]*v[3]) >> 8,
- (v[2]*v[3]) >> 8);
- endpoints[1] = SkColorSetARGB(v5, v[0], v[1], v[2]);
-}
-
-// Helper function that decodes two colors from eight values. If isRGB is true,
-// then the pointer 'v' contains six values and the last two are considered to be
-// 0xFF. If isRGB is false, then all eight values come from the pointer 'v'. This
-// corresponds to the decode procedure for the following endpoint modes:
-// kLDR_RGB_BaseOffset_ColorEndpointMode
-// kLDR_RGBA_BaseOffset_ColorEndpointMode
-//
-// If isRGB is true, then treat this as if v6 and v7 are meant to encode full alpha values.
-static inline void decode_rgba_baseoffset(const int *v, SkColor *endpoints, bool isRGB) {
- int v0 = v[0];
- int v1 = v[1];
- int v2 = v[2];
- int v3 = v[3];
- int v4 = v[4];
- int v5 = v[5];
- int v6 = isRGB ? 0xFF : v[6];
- // The 0 is here because this is an offset, not a direct value
- int v7 = isRGB ? 0 : v[7];
-
- bit_transfer_signed(&v1, &v0);
- bit_transfer_signed(&v3, &v2);
- bit_transfer_signed(&v5, &v4);
- if (!isRGB) {
- bit_transfer_signed(&v7, &v6);
- }
-
- int c[2][4];
- if ((v1 + v3 + v5) >= 0) {
- c[0][0] = v6;
- c[0][1] = v0;
- c[0][2] = v2;
- c[0][3] = v4;
-
- c[1][0] = v6 + v7;
- c[1][1] = v0 + v1;
- c[1][2] = v2 + v3;
- c[1][3] = v4 + v5;
- } else {
- c[0][0] = v6 + v7;
- c[0][1] = (v0 + v1 + v4 + v5) >> 1;
- c[0][2] = (v2 + v3 + v4 + v5) >> 1;
- c[0][3] = v4 + v5;
-
- c[1][0] = v6;
- c[1][1] = (v0 + v4) >> 1;
- c[1][2] = (v2 + v4) >> 1;
- c[1][3] = v4;
- }
-
- endpoints[0] = SkColorSetARGB(clamp_byte(c[0][0]),
- clamp_byte(c[0][1]),
- clamp_byte(c[0][2]),
- clamp_byte(c[0][3]));
-
- endpoints[1] = SkColorSetARGB(clamp_byte(c[1][0]),
- clamp_byte(c[1][1]),
- clamp_byte(c[1][2]),
- clamp_byte(c[1][3]));
-}
-
-
-// A helper class used to decode bit values from standard integer values.
-// We can't use this class with ASTCBlock because then it would need to
-// handle multi-value ranges, and it's non-trivial to lookup a range of bits
-// that splits across two different ints.
-template <typename T>
-class SkTBits {
-public:
- SkTBits(const T val) : fVal(val) { }
-
- // Returns the bit at the given position
- T operator [](const int idx) const {
- return (fVal >> idx) & 1;
- }
-
- // Returns the bits in the given range, inclusive
- T operator ()(const int end, const int start) const {
- SkASSERT(end >= start);
- return (fVal >> start) & ((1ULL << ((end - start) + 1)) - 1);
- }
-
-private:
- const T fVal;
-};
-
-// This algorithm matches the trit block decoding in the spec (Table C.2.14)
-static void decode_trit_block(int* dst, int nBits, const uint64_t &block) {
-
- SkTBits<uint64_t> blockBits(block);
-
- // According to the spec, a trit block, which contains five values,
- // has the following layout:
- //
- // 27 26 25 24 23 22 21 20 19 18 17 16
- // -----------------------------------------------
- // |T7 | m4 |T6 T5 | m3 |T4 |
- // -----------------------------------------------
- //
- // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- // --------------------------------------------------------------
- // | m2 |T3 T2 | m1 |T1 T0 | m0 |
- // --------------------------------------------------------------
- //
- // Where the m's are variable width depending on the number of bits used
- // to encode the values (anywhere from 0 to 6). Since 3^5 = 243, the extra
- // byte labeled T (whose bits are interleaved where 0 is the LSB and 7 is
- // the MSB), contains five trit values. To decode the trit values, the spec
- // says that we need to follow the following algorithm:
- //
- // if T[4:2] = 111
- // C = { T[7:5], T[1:0] }; t4 = t3 = 2
- // else
- // C = T[4:0]
- //
- // if T[6:5] = 11
- // t4 = 2; t3 = T[7]
- // else
- // t4 = T[7]; t3 = T[6:5]
- //
- // if C[1:0] = 11
- // t2 = 2; t1 = C[4]; t0 = { C[3], C[2]&~C[3] }
- // else if C[3:2] = 11
- // t2 = 2; t1 = 2; t0 = C[1:0]
- // else
- // t2 = C[4]; t1 = C[3:2]; t0 = { C[1], C[0]&~C[1] }
- //
- // The following C++ code is meant to mirror this layout and algorithm as
- // closely as possible.
-
- int m[5];
- if (0 == nBits) {
- memset(m, 0, sizeof(m));
- } else {
- SkASSERT(nBits < 8);
- m[0] = static_cast<int>(blockBits(nBits - 1, 0));
- m[1] = static_cast<int>(blockBits(2*nBits - 1 + 2, nBits + 2));
- m[2] = static_cast<int>(blockBits(3*nBits - 1 + 4, 2*nBits + 4));
- m[3] = static_cast<int>(blockBits(4*nBits - 1 + 5, 3*nBits + 5));
- m[4] = static_cast<int>(blockBits(5*nBits - 1 + 7, 4*nBits + 7));
- }
-
- int T =
- static_cast<int>(blockBits(nBits + 1, nBits)) |
- (static_cast<int>(blockBits(2*nBits + 2 + 1, 2*nBits + 2)) << 2) |
- (static_cast<int>(blockBits[3*nBits + 4] << 4)) |
- (static_cast<int>(blockBits(4*nBits + 5 + 1, 4*nBits + 5)) << 5) |
- (static_cast<int>(blockBits[5*nBits + 7] << 7));
-
- int t[5];
-
- int C;
- SkTBits<int> Tbits(T);
- if (0x7 == Tbits(4, 2)) {
- C = (Tbits(7, 5) << 2) | Tbits(1, 0);
- t[3] = t[4] = 2;
- } else {
- C = Tbits(4, 0);
- if (Tbits(6, 5) == 0x3) {
- t[4] = 2; t[3] = Tbits[7];
- } else {
- t[4] = Tbits[7]; t[3] = Tbits(6, 5);
- }
- }
-
- SkTBits<int> Cbits(C);
- if (Cbits(1, 0) == 0x3) {
- t[2] = 2;
- t[1] = Cbits[4];
- t[0] = (Cbits[3] << 1) | (Cbits[2] & (0x1 & ~(Cbits[3])));
- } else if (Cbits(3, 2) == 0x3) {
- t[2] = 2;
- t[1] = 2;
- t[0] = Cbits(1, 0);
- } else {
- t[2] = Cbits[4];
- t[1] = Cbits(3, 2);
- t[0] = (Cbits[1] << 1) | (Cbits[0] & (0x1 & ~(Cbits[1])));
- }
-
-#ifdef SK_DEBUG
- // Make sure all of the decoded values have a trit less than three
- // and a bit value within the range of the allocated bits.
- for (int i = 0; i < 5; ++i) {
- SkASSERT(t[i] < 3);
- SkASSERT(m[i] < (1 << nBits));
- }
-#endif
-
- for (int i = 0; i < 5; ++i) {
- *dst = (t[i] << nBits) + m[i];
- ++dst;
- }
-}
-
-// This algorithm matches the quint block decoding in the spec (Table C.2.15)
-static void decode_quint_block(int* dst, int nBits, const uint64_t &block) {
- SkTBits<uint64_t> blockBits(block);
-
- // According to the spec, a quint block, which contains three values,
- // has the following layout:
- //
- //
- // 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- // --------------------------------------------------------------------------
- // |Q6 Q5 | m2 |Q4 Q3 | m1 |Q2 Q1 Q0 | m0 |
- // --------------------------------------------------------------------------
- //
- // Where the m's are variable width depending on the number of bits used
- // to encode the values (anywhere from 0 to 4). Since 5^3 = 125, the extra
- // 7-bit value labeled Q (whose bits are interleaved where 0 is the LSB and 6 is
- // the MSB), contains three quint values. To decode the quint values, the spec
- // says that we need to follow the following algorithm:
- //
- // if Q[2:1] = 11 and Q[6:5] = 00
- // q2 = { Q[0], Q[4]&~Q[0], Q[3]&~Q[0] }; q1 = q0 = 4
- // else
- // if Q[2:1] = 11
- // q2 = 4; C = { Q[4:3], ~Q[6:5], Q[0] }
- // else
- // q2 = T[6:5]; C = Q[4:0]
- //
- // if C[2:0] = 101
- // q1 = 4; q0 = C[4:3]
- // else
- // q1 = C[4:3]; q0 = C[2:0]
- //
- // The following C++ code is meant to mirror this layout and algorithm as
- // closely as possible.
-
- int m[3];
- if (0 == nBits) {
- memset(m, 0, sizeof(m));
- } else {
- SkASSERT(nBits < 8);
- m[0] = static_cast<int>(blockBits(nBits - 1, 0));
- m[1] = static_cast<int>(blockBits(2*nBits - 1 + 3, nBits + 3));
- m[2] = static_cast<int>(blockBits(3*nBits - 1 + 5, 2*nBits + 5));
- }
-
- int Q =
- static_cast<int>(blockBits(nBits + 2, nBits)) |
- (static_cast<int>(blockBits(2*nBits + 3 + 1, 2*nBits + 3)) << 3) |
- (static_cast<int>(blockBits(3*nBits + 5 + 1, 3*nBits + 5)) << 5);
-
- int q[3];
- SkTBits<int> Qbits(Q); // quantum?
-
- if (Qbits(2, 1) == 0x3 && Qbits(6, 5) == 0) {
- const int notBitZero = (0x1 & ~(Qbits[0]));
- q[2] = (Qbits[0] << 2) | ((Qbits[4] & notBitZero) << 1) | (Qbits[3] & notBitZero);
- q[1] = 4;
- q[0] = 4;
- } else {
- int C;
- if (Qbits(2, 1) == 0x3) {
- q[2] = 4;
- C = (Qbits(4, 3) << 3) | ((0x3 & ~(Qbits(6, 5))) << 1) | Qbits[0];
- } else {
- q[2] = Qbits(6, 5);
- C = Qbits(4, 0);
- }
-
- SkTBits<int> Cbits(C);
- if (Cbits(2, 0) == 0x5) {
- q[1] = 4;
- q[0] = Cbits(4, 3);
- } else {
- q[1] = Cbits(4, 3);
- q[0] = Cbits(2, 0);
- }
- }
-
-#ifdef SK_DEBUG
- for (int i = 0; i < 3; ++i) {
- SkASSERT(q[i] < 5);
- SkASSERT(m[i] < (1 << nBits));
- }
-#endif
-
- for (int i = 0; i < 3; ++i) {
- *dst = (q[i] << nBits) + m[i];
- ++dst;
- }
-}
-
-// Function that decodes a sequence of integers stored as an ISE (Integer
-// Sequence Encoding) bit stream. The full details of this function are outlined
-// in section C.2.12 of the ASTC spec. A brief overview is as follows:
-//
-// - Each integer in the sequence is bounded by a specific range r.
-// - The range of each value determines the way the bit stream is interpreted,
-// - If the range is a power of two, then the sequence is a sequence of bits
-// - If the range is of the form 3*2^n, then the sequence is stored as a
-// sequence of blocks, each block contains 5 trits and 5 bit sequences, which
-// decodes into 5 values.
-// - Similarly, if the range is of the form 5*2^n, then the sequence is stored as a
-// sequence of blocks, each block contains 3 quints and 3 bit sequences, which
-// decodes into 3 values.
-static bool decode_integer_sequence(
- int* dst, // The array holding the destination bits
- int dstSize, // The maximum size of the array
- int nVals, // The number of values that we'd like to decode
- const ASTCBlock &block, // The block that we're decoding from
- int startBit, // The bit from which we're going to do the reading
- int endBit, // The bit at which we stop reading (not inclusive)
- bool bReadForward, // If true, then read LSB -> MSB, else read MSB -> LSB
- int nBits, // The number of bits representing this encoding
- int nTrits, // The number of trits representing this encoding
- int nQuints // The number of quints representing this encoding
-) {
- // If we want more values than we have, then fail.
- if (nVals > dstSize) {
- return false;
- }
-
- ASTCBlock src = block;
-
- if (!bReadForward) {
- src.reverse();
- startBit = 128 - startBit;
- endBit = 128 - endBit;
- }
-
- while (nVals > 0) {
-
- if (nTrits > 0) {
- SkASSERT(0 == nQuints);
-
- int endBlockBit = startBit + 8 + 5*nBits;
- if (endBlockBit > endBit) {
- endBlockBit = endBit;
- }
-
- // Trit blocks are three values large.
- int trits[5];
- decode_trit_block(trits, nBits, read_astc_bits(src, startBit, endBlockBit));
- memcpy(dst, trits, SkMin32(nVals, 5)*sizeof(int));
-
- dst += 5;
- nVals -= 5;
- startBit = endBlockBit;
-
- } else if (nQuints > 0) {
- SkASSERT(0 == nTrits);
-
- int endBlockBit = startBit + 7 + 3*nBits;
- if (endBlockBit > endBit) {
- endBlockBit = endBit;
- }
-
- // Quint blocks are three values large
- int quints[3];
- decode_quint_block(quints, nBits, read_astc_bits(src, startBit, endBlockBit));
- memcpy(dst, quints, SkMin32(nVals, 3)*sizeof(int));
-
- dst += 3;
- nVals -= 3;
- startBit = endBlockBit;
-
- } else {
- // Just read the bits, but don't read more than we have...
- int endValBit = startBit + nBits;
- if (endValBit > endBit) {
- endValBit = endBit;
- }
-
- SkASSERT(endValBit - startBit < 31);
- *dst = static_cast<int>(read_astc_bits(src, startBit, endValBit));
- ++dst;
- --nVals;
- startBit = endValBit;
- }
- }
-
- return true;
-}
-
-// Helper function that unquantizes some (seemingly random) generated
-// numbers... meant to match the ASTC hardware. This function is used
-// to unquantize both colors (Table C.2.16) and weights (Table C.2.26)
-static inline int unquantize_value(unsigned mask, int A, int B, int C, int D) {
- int T = D * C + B;
- T = T ^ A;
- T = (A & mask) | (T >> 2);
- SkASSERT(T < 256);
- return T;
-}
-
-// Helper function to replicate the bits in x that represents an oldPrec
-// precision integer into a prec precision integer. For example:
-// 255 == replicate_bits(7, 3, 8);
-static inline int replicate_bits(int x, int oldPrec, int prec) {
- while (oldPrec < prec) {
- const int toShift = SkMin32(prec-oldPrec, oldPrec);
- x = (x << toShift) | (x >> (oldPrec - toShift));
- oldPrec += toShift;
- }
-
- // Make sure that no bits are set outside the desired precision.
- SkASSERT((-(1 << prec) & x) == 0);
- return x;
-}
-
-// Returns the unquantized value of a color that's represented only as
-// a set of bits.
-static inline int unquantize_bits_color(int val, int nBits) {
- return replicate_bits(val, nBits, 8);
-}
-
-// Returns the unquantized value of a color that's represented as a
-// trit followed by nBits bits. This algorithm follows the sequence
-// defined in section C.2.13 of the ASTC spec.
-static inline int unquantize_trit_color(int val, int nBits) {
- SkASSERT(nBits > 0);
- SkASSERT(nBits < 7);
-
- const int D = (val >> nBits) & 0x3;
- SkASSERT(D < 3);
-
- const int A = -(val & 0x1) & 0x1FF;
-
- static const int Cvals[6] = { 204, 93, 44, 22, 11, 5 };
- const int C = Cvals[nBits - 1];
-
- int B = 0;
- const SkTBits<int> valBits(val);
- switch (nBits) {
- case 1:
- B = 0;
- break;
-
- case 2: {
- const int b = valBits[1];
- B = (b << 1) | (b << 2) | (b << 4) | (b << 8);
- }
- break;
-
- case 3: {
- const int cb = valBits(2, 1);
- B = cb | (cb << 2) | (cb << 7);
- }
- break;
-
- case 4: {
- const int dcb = valBits(3, 1);
- B = dcb | (dcb << 6);
- }
- break;
-
- case 5: {
- const int edcb = valBits(4, 1);
- B = (edcb << 5) | (edcb >> 2);
- }
- break;
-
- case 6: {
- const int fedcb = valBits(5, 1);
- B = (fedcb << 4) | (fedcb >> 4);
- }
- break;
- }
-
- return unquantize_value(0x80, A, B, C, D);
-}
-
-// Returns the unquantized value of a color that's represented as a
-// quint followed by nBits bits. This algorithm follows the sequence
-// defined in section C.2.13 of the ASTC spec.
-static inline int unquantize_quint_color(int val, int nBits) {
- const int D = (val >> nBits) & 0x7;
- SkASSERT(D < 5);
-
- const int A = -(val & 0x1) & 0x1FF;
-
- static const int Cvals[5] = { 113, 54, 26, 13, 6 };
- SkASSERT(nBits > 0);
- SkASSERT(nBits < 6);
-
- const int C = Cvals[nBits - 1];
-
- int B = 0;
- const SkTBits<int> valBits(val);
- switch (nBits) {
- case 1:
- B = 0;
- break;
-
- case 2: {
- const int b = valBits[1];
- B = (b << 2) | (b << 3) | (b << 8);
- }
- break;
-
- case 3: {
- const int cb = valBits(2, 1);
- B = (cb >> 1) | (cb << 1) | (cb << 7);
- }
- break;
-
- case 4: {
- const int dcb = valBits(3, 1);
- B = (dcb >> 1) | (dcb << 6);
- }
- break;
-
- case 5: {
- const int edcb = valBits(4, 1);
- B = (edcb << 5) | (edcb >> 3);
- }
- break;
- }
-
- return unquantize_value(0x80, A, B, C, D);
-}
-
-// This algorithm takes a list of integers, stored in vals, and unquantizes them
-// in place. This follows the algorithm laid out in section C.2.13 of the ASTC spec.
-static void unquantize_colors(int *vals, int nVals, int nBits, int nTrits, int nQuints) {
- for (int i = 0; i < nVals; ++i) {
- if (nTrits > 0) {
- SkASSERT(nQuints == 0);
- vals[i] = unquantize_trit_color(vals[i], nBits);
- } else if (nQuints > 0) {
- SkASSERT(nTrits == 0);
- vals[i] = unquantize_quint_color(vals[i], nBits);
- } else {
- SkASSERT(nQuints == 0 && nTrits == 0);
- vals[i] = unquantize_bits_color(vals[i], nBits);
- }
- }
-}
-
-// Returns an interpolated value between c0 and c1 based on the weight. This
-// follows the algorithm laid out in section C.2.19 of the ASTC spec.
-static int interpolate_channel(int c0, int c1, int weight) {
- SkASSERT(0 <= c0 && c0 < 256);
- SkASSERT(0 <= c1 && c1 < 256);
-
- c0 = (c0 << 8) | c0;
- c1 = (c1 << 8) | c1;
-
- const int result = ((c0*(64 - weight) + c1*weight + 32) / 64) >> 8;
-
- if (result > 255) {
- return 255;
- }
-
- SkASSERT(result >= 0);
- return result;
-}
-
-// Returns an interpolated color between the two endpoints based on the weight.
-static SkColor interpolate_endpoints(const SkColor endpoints[2], int weight) {
- return SkColorSetARGB(
- interpolate_channel(SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight),
- interpolate_channel(SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight),
- interpolate_channel(SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight),
- interpolate_channel(SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight));
-}
-
-// Returns an interpolated color between the two endpoints based on the weight.
-// It uses separate weights for the channel depending on the value of the 'plane'
-// variable. By default, all channels will use weight 0, and the value of plane
-// means that weight1 will be used for:
-// 0: red
-// 1: green
-// 2: blue
-// 3: alpha
-static SkColor interpolate_dual_endpoints(
- const SkColor endpoints[2], int weight0, int weight1, int plane) {
- int a = interpolate_channel(SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight0);
- int r = interpolate_channel(SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight0);
- int g = interpolate_channel(SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight0);
- int b = interpolate_channel(SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight0);
-
- switch (plane) {
-
- case 0:
- r = interpolate_channel(
- SkColorGetR(endpoints[0]), SkColorGetR(endpoints[1]), weight1);
- break;
-
- case 1:
- g = interpolate_channel(
- SkColorGetG(endpoints[0]), SkColorGetG(endpoints[1]), weight1);
- break;
-
- case 2:
- b = interpolate_channel(
- SkColorGetB(endpoints[0]), SkColorGetB(endpoints[1]), weight1);
- break;
-
- case 3:
- a = interpolate_channel(
- SkColorGetA(endpoints[0]), SkColorGetA(endpoints[1]), weight1);
- break;
-
- default:
- SkDEBUGFAIL("Plane should be 0-3");
- break;
- }
-
- return SkColorSetARGB(a, r, g, b);
-}
-
-// A struct of decoded values that we use to carry around information
-// about the block. dimX and dimY are the dimension in texels of the block,
-// for which there is only a limited subset of valid values:
-//
-// 4x4, 5x4, 5x5, 6x5, 6x6, 8x5, 8x6, 8x8, 10x5, 10x6, 10x8, 10x10, 12x10, 12x12
-
-struct ASTCDecompressionData {
- ASTCDecompressionData(int dimX, int dimY) : fDimX(dimX), fDimY(dimY) { }
- const int fDimX; // the X dimension of the decompressed block
- const int fDimY; // the Y dimension of the decompressed block
- ASTCBlock fBlock; // the block data
- int fBlockMode; // the block header that contains the block mode.
-
- bool fDualPlaneEnabled; // is this block compressing dual weight planes?
- int fDualPlane; // the independent plane in dual plane mode.
-
- bool fVoidExtent; // is this block a single color?
- bool fError; // does this block have an error encoding?
-
- int fWeightDimX; // the x dimension of the weight grid
- int fWeightDimY; // the y dimension of the weight grid
-
- int fWeightBits; // the number of bits used for each weight value
- int fWeightTrits; // the number of trits used for each weight value
- int fWeightQuints; // the number of quints used for each weight value
-
- int fPartCount; // the number of partitions in this block
- int fPartIndex; // the partition index: only relevant if fPartCount > 0
-
- // CEM values can be anything in the range 0-15, and each corresponds to a different
- // mode that represents the color data. We only support LDR modes.
- enum ColorEndpointMode {
- kLDR_Luminance_Direct_ColorEndpointMode = 0,
- kLDR_Luminance_BaseOffset_ColorEndpointMode = 1,
- kHDR_Luminance_LargeRange_ColorEndpointMode = 2,
- kHDR_Luminance_SmallRange_ColorEndpointMode = 3,
- kLDR_LuminanceAlpha_Direct_ColorEndpointMode = 4,
- kLDR_LuminanceAlpha_BaseOffset_ColorEndpointMode = 5,
- kLDR_RGB_BaseScale_ColorEndpointMode = 6,
- kHDR_RGB_BaseScale_ColorEndpointMode = 7,
- kLDR_RGB_Direct_ColorEndpointMode = 8,
- kLDR_RGB_BaseOffset_ColorEndpointMode = 9,
- kLDR_RGB_BaseScaleWithAlpha_ColorEndpointMode = 10,
- kHDR_RGB_ColorEndpointMode = 11,
- kLDR_RGBA_Direct_ColorEndpointMode = 12,
- kLDR_RGBA_BaseOffset_ColorEndpointMode = 13,
- kHDR_RGB_LDRAlpha_ColorEndpointMode = 14,
- kHDR_RGB_HDRAlpha_ColorEndpointMode = 15
- };
- static const int kMaxColorEndpointModes = 16;
-
- // the color endpoint modes for this block.
- static const int kMaxPartitions = 4;
- ColorEndpointMode fCEM[kMaxPartitions];
-
- int fColorStartBit; // The bit position of the first bit of the color data
- int fColorEndBit; // The bit position of the last *possible* bit of the color data
-
- // Returns the number of partitions for this block.
- int numPartitions() const {
- return fPartCount;
- }
-
- // Returns the total number of weight values that are stored in this block
- int numWeights() const {
- return fWeightDimX * fWeightDimY * (fDualPlaneEnabled ? 2 : 1);
- }
-
-#ifdef SK_DEBUG
- // Returns the maximum value that any weight can take. We really only use
- // this function for debugging.
- int maxWeightValue() const {
- int maxVal = (1 << fWeightBits);
- if (fWeightTrits > 0) {
- SkASSERT(0 == fWeightQuints);
- maxVal *= 3;
- } else if (fWeightQuints > 0) {
- SkASSERT(0 == fWeightTrits);
- maxVal *= 5;
- }
- return maxVal - 1;
- }
-#endif
-
- // The number of bits needed to represent the texel weight data. This
- // comes from the 'data size determination' section of the ASTC spec (C.2.22)
- int numWeightBits() const {
- const int nWeights = this->numWeights();
- return
- ((nWeights*8*fWeightTrits + 4) / 5) +
- ((nWeights*7*fWeightQuints + 2) / 3) +
- (nWeights*fWeightBits);
- }
-
- // Returns the number of color values stored in this block. The number of
- // values stored is directly a function of the color endpoint modes.
- int numColorValues() const {
- int numValues = 0;
- for (int i = 0; i < this->numPartitions(); ++i) {
- int cemInt = static_cast<int>(fCEM[i]);
- numValues += ((cemInt >> 2) + 1) * 2;
- }
-
- return numValues;
- }
-
- // Figures out the number of bits available for color values, and fills
- // in the maximum encoding that will fit the number of color values that
- // we need. Returns false on error. (See section C.2.22 of the spec)
- bool getColorValueEncoding(int *nBits, int *nTrits, int *nQuints) const {
- if (nullptr == nBits || nullptr == nTrits || nullptr == nQuints) {
- return false;
- }
-
- const int nColorVals = this->numColorValues();
- if (nColorVals <= 0) {
- return false;
- }
-
- const int colorBits = fColorEndBit - fColorStartBit;
- SkASSERT(colorBits > 0);
-
- // This is the minimum amount of accuracy required by the spec.
- if (colorBits < ((13 * nColorVals + 4) / 5)) {
- return false;
- }
-
- // Values can be represented as at most 8-bit values.
- // !SPEED! place this in a lookup table based on colorBits and nColorVals
- for (int i = 255; i > 0; --i) {
- int range = i + 1;
- int bits = 0, trits = 0, quints = 0;
- bool valid = false;
- if (SkIsPow2(range)) {
- bits = bits_for_range(range);
- valid = true;
- } else if ((range % 3) == 0 && SkIsPow2(range/3)) {
- trits = 1;
- bits = bits_for_range(range/3);
- valid = true;
- } else if ((range % 5) == 0 && SkIsPow2(range/5)) {
- quints = 1;
- bits = bits_for_range(range/5);
- valid = true;
- }
-
- if (valid) {
- const int actualColorBits =
- ((nColorVals*8*trits + 4) / 5) +
- ((nColorVals*7*quints + 2) / 3) +
- (nColorVals*bits);
- if (actualColorBits <= colorBits) {
- *nTrits = trits;
- *nQuints = quints;
- *nBits = bits;
- return true;
- }
- }
- }
-
- return false;
- }
-
- // Converts the sequence of color values into endpoints. The algorithm here
- // corresponds to the values determined by section C.2.14 of the ASTC spec
- void colorEndpoints(SkColor endpoints[4][2], const int* colorValues) const {
- for (int i = 0; i < this->numPartitions(); ++i) {
- switch (fCEM[i]) {
- case kLDR_Luminance_Direct_ColorEndpointMode: {
- const int* v = colorValues;
- endpoints[i][0] = SkColorSetARGB(0xFF, v[0], v[0], v[0]);
- endpoints[i][1] = SkColorSetARGB(0xFF, v[1], v[1], v[1]);
-
- colorValues += 2;
- }
- break;
-
- case kLDR_Luminance_BaseOffset_ColorEndpointMode: {
- const int* v = colorValues;
- const int L0 = (v[0] >> 2) | (v[1] & 0xC0);
- const int L1 = clamp_byte(L0 + (v[1] & 0x3F));
-
- endpoints[i][0] = SkColorSetARGB(0xFF, L0, L0, L0);
- endpoints[i][1] = SkColorSetARGB(0xFF, L1, L1, L1);
-
- colorValues += 2;
- }
- break;
-
- case kLDR_LuminanceAlpha_Direct_ColorEndpointMode: {
- const int* v = colorValues;
-
- endpoints[i][0] = SkColorSetARGB(v[2], v[0], v[0], v[0]);
- endpoints[i][1] = SkColorSetARGB(v[3], v[1], v[1], v[1]);
-
- colorValues += 4;
- }
- break;
-
- case kLDR_LuminanceAlpha_BaseOffset_ColorEndpointMode: {
- int v0 = colorValues[0];
- int v1 = colorValues[1];
- int v2 = colorValues[2];
- int v3 = colorValues[3];
-
- bit_transfer_signed(&v1, &v0);
- bit_transfer_signed(&v3, &v2);
-
- endpoints[i][0] = SkColorSetARGB(v2, v0, v0, v0);
- endpoints[i][1] = SkColorSetARGB(
- clamp_byte(v3+v2),
- clamp_byte(v1+v0),
- clamp_byte(v1+v0),
- clamp_byte(v1+v0));
-
- colorValues += 4;
- }
- break;
-
- case kLDR_RGB_BaseScale_ColorEndpointMode: {
- decode_rgba_basescale(colorValues, endpoints[i], true);
- colorValues += 4;
- }
- break;
-
- case kLDR_RGB_Direct_ColorEndpointMode: {
- decode_rgba_direct(colorValues, endpoints[i], true);
- colorValues += 6;
- }
- break;
-
- case kLDR_RGB_BaseOffset_ColorEndpointMode: {
- decode_rgba_baseoffset(colorValues, endpoints[i], true);
- colorValues += 6;
- }
- break;
-
- case kLDR_RGB_BaseScaleWithAlpha_ColorEndpointMode: {
- decode_rgba_basescale(colorValues, endpoints[i], false);
- colorValues += 6;
- }
- break;
-
- case kLDR_RGBA_Direct_ColorEndpointMode: {
- decode_rgba_direct(colorValues, endpoints[i], false);
- colorValues += 8;
- }
- break;
-
- case kLDR_RGBA_BaseOffset_ColorEndpointMode: {
- decode_rgba_baseoffset(colorValues, endpoints[i], false);
- colorValues += 8;
- }
- break;
-
- default:
- SkDEBUGFAIL("HDR mode unsupported! This should be caught sooner.");
- break;
- }
- }
- }
-
- // Follows the procedure from section C.2.17 of the ASTC specification
- int unquantizeWeight(int x) const {
- SkASSERT(x <= this->maxWeightValue());
-
- const int D = (x >> fWeightBits) & 0x7;
- const int A = -(x & 0x1) & 0x7F;
-
- SkTBits<int> xbits(x);
-
- int T = 0;
- if (fWeightTrits > 0) {
- SkASSERT(0 == fWeightQuints);
- switch (fWeightBits) {
- case 0: {
- // x is a single trit
- SkASSERT(x < 3);
-
- static const int kUnquantizationTable[3] = { 0, 32, 63 };
- T = kUnquantizationTable[x];
- }
- break;
-
- case 1: {
- const int B = 0;
- const int C = 50;
- T = unquantize_value(0x20, A, B, C, D);
- }
- break;
-
- case 2: {
- const int b = xbits[1];
- const int B = b | (b << 2) | (b << 6);
- const int C = 23;
- T = unquantize_value(0x20, A, B, C, D);
- }
- break;
-
- case 3: {
- const int cb = xbits(2, 1);
- const int B = cb | (cb << 5);
- const int C = 11;
- T = unquantize_value(0x20, A, B, C, D);
- }
- break;
-
- default:
- SkDEBUGFAIL("Too many bits for trit encoding");
- break;
- }
-
- } else if (fWeightQuints > 0) {
- SkASSERT(0 == fWeightTrits);
- switch (fWeightBits) {
- case 0: {
- // x is a single quint
- SkASSERT(x < 5);
-
- static const int kUnquantizationTable[5] = { 0, 16, 32, 47, 63 };
- T = kUnquantizationTable[x];
- }
- break;
-
- case 1: {
- const int B = 0;
- const int C = 28;
- T = unquantize_value(0x20, A, B, C, D);
- }
- break;
-
- case 2: {
- const int b = xbits[1];
- const int B = (b << 1) | (b << 6);
- const int C = 13;
- T = unquantize_value(0x20, A, B, C, D);
- }
- break;
-
- default:
- SkDEBUGFAIL("Too many bits for quint encoding");
- break;
- }
- } else {
- SkASSERT(0 == fWeightTrits);
- SkASSERT(0 == fWeightQuints);
-
- T = replicate_bits(x, fWeightBits, 6);
- }
-
- // This should bring the value within [0, 63]..
- SkASSERT(T <= 63);
-
- if (T > 32) {
- T += 1;
- }
-
- SkASSERT(T <= 64);
-
- return T;
- }
-
- // Returns the weight at the associated index. If the index is out of bounds, it
- // returns zero. It also chooses the weight appropriately based on the given dual
- // plane.
- int getWeight(const int* unquantizedWeights, int idx, bool dualPlane) const {
- const int maxIdx = (fDualPlaneEnabled ? 2 : 1) * fWeightDimX * fWeightDimY - 1;
- if (fDualPlaneEnabled) {
- const int effectiveIdx = 2*idx + (dualPlane ? 1 : 0);
- if (effectiveIdx > maxIdx) {
- return 0;
- }
- return unquantizedWeights[effectiveIdx];
- }
-
- SkASSERT(!dualPlane);
-
- if (idx > maxIdx) {
- return 0;
- } else {
- return unquantizedWeights[idx];
- }
- }
-
- // This computes the effective weight at location (s, t) of the block. This
- // weight is computed by sampling the texel weight grid (it's usually not 1-1), and
- // then applying a bilerp. The algorithm outlined here follows the algorithm
- // defined in section C.2.18 of the ASTC spec.
- int infillWeight(const int* unquantizedValues, int s, int t, bool dualPlane) const {
- const int Ds = (1024 + fDimX/2) / (fDimX - 1);
- const int Dt = (1024 + fDimY/2) / (fDimY - 1);
-
- const int cs = Ds * s;
- const int ct = Dt * t;
-
- const int gs = (cs*(fWeightDimX - 1) + 32) >> 6;
- const int gt = (ct*(fWeightDimY - 1) + 32) >> 6;
-
- const int js = gs >> 4;
- const int jt = gt >> 4;
-
- const int fs = gs & 0xF;
- const int ft = gt & 0xF;
-
- const int idx = js + jt*fWeightDimX;
- const int p00 = this->getWeight(unquantizedValues, idx, dualPlane);
- const int p01 = this->getWeight(unquantizedValues, idx + 1, dualPlane);
- const int p10 = this->getWeight(unquantizedValues, idx + fWeightDimX, dualPlane);
- const int p11 = this->getWeight(unquantizedValues, idx + fWeightDimX + 1, dualPlane);
-
- const int w11 = (fs*ft + 8) >> 4;
- const int w10 = ft - w11;
- const int w01 = fs - w11;
- const int w00 = 16 - fs - ft + w11;
-
- const int weight = (p00*w00 + p01*w01 + p10*w10 + p11*w11 + 8) >> 4;
- SkASSERT(weight <= 64);
- return weight;
- }
-
- // Unquantizes the decoded texel weights as described in section C.2.17 of
- // the ASTC specification. Additionally, it populates texelWeights with
- // the expanded weight grid, which is computed according to section C.2.18
- void texelWeights(int texelWeights[2][12][12], const int* texelValues) const {
- // Unquantized texel weights...
- int unquantizedValues[144*2]; // 12x12 blocks with dual plane decoding...
- SkASSERT(this->numWeights() <= 144*2);
-
- // Unquantize the weights and cache them
- for (int j = 0; j < this->numWeights(); ++j) {
- unquantizedValues[j] = this->unquantizeWeight(texelValues[j]);
- }
-
- // Do weight infill...
- for (int y = 0; y < fDimY; ++y) {
- for (int x = 0; x < fDimX; ++x) {
- texelWeights[0][x][y] = this->infillWeight(unquantizedValues, x, y, false);
- if (fDualPlaneEnabled) {
- texelWeights[1][x][y] = this->infillWeight(unquantizedValues, x, y, true);
- }
- }
- }
- }
-
- // Returns the partition for the texel located at position (x, y).
- // Adapted from C.2.21 of the ASTC specification
- int getPartition(int x, int y) const {
- const int partitionCount = this->numPartitions();
- int seed = fPartIndex;
- if ((fDimX * fDimY) < 31) {
- x <<= 1;
- y <<= 1;
- }
-
- seed += (partitionCount - 1) * 1024;
-
- uint32_t p = seed;
- p ^= p >> 15; p -= p << 17; p += p << 7; p += p << 4;
- p ^= p >> 5; p += p << 16; p ^= p >> 7; p ^= p >> 3;
- p ^= p << 6; p ^= p >> 17;
-
- uint32_t rnum = p;
- uint8_t seed1 = rnum & 0xF;
- uint8_t seed2 = (rnum >> 4) & 0xF;
- uint8_t seed3 = (rnum >> 8) & 0xF;
- uint8_t seed4 = (rnum >> 12) & 0xF;
- uint8_t seed5 = (rnum >> 16) & 0xF;
- uint8_t seed6 = (rnum >> 20) & 0xF;
- uint8_t seed7 = (rnum >> 24) & 0xF;
- uint8_t seed8 = (rnum >> 28) & 0xF;
- uint8_t seed9 = (rnum >> 18) & 0xF;
- uint8_t seed10 = (rnum >> 22) & 0xF;
- uint8_t seed11 = (rnum >> 26) & 0xF;
- uint8_t seed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;
-
- seed1 *= seed1; seed2 *= seed2;
- seed3 *= seed3; seed4 *= seed4;
- seed5 *= seed5; seed6 *= seed6;
- seed7 *= seed7; seed8 *= seed8;
- seed9 *= seed9; seed10 *= seed10;
- seed11 *= seed11; seed12 *= seed12;
-
- int sh1, sh2, sh3;
- if (0 != (seed & 1)) {
- sh1 = (0 != (seed & 2))? 4 : 5;
- sh2 = (partitionCount == 3)? 6 : 5;
- } else {
- sh1 = (partitionCount==3)? 6 : 5;
- sh2 = (0 != (seed & 2))? 4 : 5;
- }
- sh3 = (0 != (seed & 0x10))? sh1 : sh2;
-
- seed1 >>= sh1; seed2 >>= sh2; seed3 >>= sh1; seed4 >>= sh2;
- seed5 >>= sh1; seed6 >>= sh2; seed7 >>= sh1; seed8 >>= sh2;
- seed9 >>= sh3; seed10 >>= sh3; seed11 >>= sh3; seed12 >>= sh3;
-
- const int z = 0;
- int a = seed1*x + seed2*y + seed11*z + (rnum >> 14);
- int b = seed3*x + seed4*y + seed12*z + (rnum >> 10);
- int c = seed5*x + seed6*y + seed9 *z + (rnum >> 6);
- int d = seed7*x + seed8*y + seed10*z + (rnum >> 2);
-
- a &= 0x3F;
- b &= 0x3F;
- c &= 0x3F;
- d &= 0x3F;
-
- if (partitionCount < 4) {
- d = 0;
- }
-
- if (partitionCount < 3) {
- c = 0;
- }
-
- if (a >= b && a >= c && a >= d) {
- return 0;
- } else if (b >= c && b >= d) {
- return 1;
- } else if (c >= d) {
- return 2;
- } else {
- return 3;
- }
- }
-
- // Performs the proper interpolation of the texel based on the
- // endpoints and weights.
- SkColor getTexel(const SkColor endpoints[4][2],
- const int weights[2][12][12],
- int x, int y) const {
- int part = 0;
- if (this->numPartitions() > 1) {
- part = this->getPartition(x, y);
- }
-
- SkColor result;
- if (fDualPlaneEnabled) {
- result = interpolate_dual_endpoints(
- endpoints[part], weights[0][x][y], weights[1][x][y], fDualPlane);
- } else {
- result = interpolate_endpoints(endpoints[part], weights[0][x][y]);
- }
-
-#if 1
- // !FIXME! if we're writing directly to a bitmap, then we don't need
- // to swap the red and blue channels, but since we're usually being used
- // by the SkImageDecoder_astc module, the results are expected to be in RGBA.
- result = SkColorSetARGB(
- SkColorGetA(result), SkColorGetB(result), SkColorGetG(result), SkColorGetR(result));
-#endif
-
- return result;
- }
-
- void decode() {
- // First decode the block mode.
- this->decodeBlockMode();
-
- // Now we can decode the partition information.
- fPartIndex = static_cast<int>(read_astc_bits(fBlock, 11, 23));
- fPartCount = (fPartIndex & 0x3) + 1;
- fPartIndex >>= 2;
-
- // This is illegal
- if (fDualPlaneEnabled && this->numPartitions() == 4) {
- fError = true;
- return;
- }
-
- // Based on the partition info, we can decode the color information.
- this->decodeColorData();
- }
-
- // Decodes the dual plane based on the given bit location. The final
- // location, if the dual plane is enabled, is also the end of our color data.
- // This function is only meant to be used from this->decodeColorData()
- void decodeDualPlane(int bitLoc) {
- if (fDualPlaneEnabled) {
- fDualPlane = static_cast<int>(read_astc_bits(fBlock, bitLoc - 2, bitLoc));
- fColorEndBit = bitLoc - 2;
- } else {
- fColorEndBit = bitLoc;
- }
- }
-
- // Decodes the color information based on the ASTC spec.
- void decodeColorData() {
-
- // By default, the last color bit is at the end of the texel weights
- const int lastWeight = 128 - this->numWeightBits();
-
- // If we have a dual plane then it will be at this location, too.
- int dualPlaneBitLoc = lastWeight;
-
- // If there's only one partition, then our job is (relatively) easy.
- if (this->numPartitions() == 1) {
- fCEM[0] = static_cast<ColorEndpointMode>(read_astc_bits(fBlock, 13, 17));
- fColorStartBit = 17;
-
- // Handle dual plane mode...
- this->decodeDualPlane(dualPlaneBitLoc);
-
- return;
- }
-
- // If we have more than one partition, then we need to make
- // room for the partition index.
- fColorStartBit = 29;
-
- // Read the base CEM. If it's zero, then we have no additional
- // CEM data and the endpoints for each partition share the same CEM.
- const int baseCEM = static_cast<int>(read_astc_bits(fBlock, 23, 25));
- if (0 == baseCEM) {
-
- const ColorEndpointMode sameCEM =
- static_cast<ColorEndpointMode>(read_astc_bits(fBlock, 25, 29));
-
- for (int i = 0; i < kMaxPartitions; ++i) {
- fCEM[i] = sameCEM;
- }
-
- // Handle dual plane mode...
- this->decodeDualPlane(dualPlaneBitLoc);
-
- return;
- }
-
- // Move the dual plane selector bits down based on how many
- // partitions the block contains.
- switch (this->numPartitions()) {
- case 2:
- dualPlaneBitLoc -= 2;
- break;
-
- case 3:
- dualPlaneBitLoc -= 5;
- break;
-
- case 4:
- dualPlaneBitLoc -= 8;
- break;
-
- default:
- SkDEBUGFAIL("Internal ASTC decoding error.");
- break;
- }
-
- // The rest of the CEM config will be between the dual plane bit selector
- // and the texel weight grid.
- const int lowCEM = static_cast<int>(read_astc_bits(fBlock, 23, 29));
- SkASSERT(lastWeight >= dualPlaneBitLoc);
- SkASSERT(lastWeight - dualPlaneBitLoc < 31);
- int fullCEM = static_cast<int>(read_astc_bits(fBlock, dualPlaneBitLoc, lastWeight));
-
- // Attach the config at the end of the weight grid to the CEM values
- // in the beginning of the block.
- fullCEM = (fullCEM << 6) | lowCEM;
-
- // Ignore the two least significant bits, since those are our baseCEM above.
- fullCEM = fullCEM >> 2;
-
- int C[kMaxPartitions]; // Next, decode C and M from the spec (Table C.2.12)
- for (int i = 0; i < this->numPartitions(); ++i) {
- C[i] = fullCEM & 1;
- fullCEM = fullCEM >> 1;
- }
-
- int M[kMaxPartitions];
- for (int i = 0; i < this->numPartitions(); ++i) {
- M[i] = fullCEM & 0x3;
- fullCEM = fullCEM >> 2;
- }
-
- // Construct our CEMs..
- SkASSERT(baseCEM > 0);
- for (int i = 0; i < this->numPartitions(); ++i) {
- int cem = (baseCEM - 1) * 4;
- cem += (0 == C[i])? 0 : 4;
- cem += M[i];
-
- SkASSERT(cem < 16);
- fCEM[i] = static_cast<ColorEndpointMode>(cem);
- }
-
- // Finally, if we have dual plane mode, then read the plane selector.
- this->decodeDualPlane(dualPlaneBitLoc);
- }
-
- // Decodes the block mode. This function determines whether or not we use
- // dual plane encoding, the size of the texel weight grid, and the number of
- // bits, trits and quints that are used to encode it. For more information,
- // see section C.2.10 of the ASTC spec.
- //
- // For 2D blocks, the Block Mode field is laid out as follows:
- //
- // -------------------------------------------------------------------------
- // 10 9 8 7 6 5 4 3 2 1 0 Width Height Notes
- // -------------------------------------------------------------------------
- // D H B A R0 0 0 R2 R1 B+4 A+2
- // D H B A R0 0 1 R2 R1 B+8 A+2
- // D H B A R0 1 0 R2 R1 A+2 B+8
- // D H 0 B A R0 1 1 R2 R1 A+2 B+6
- // D H 1 B A R0 1 1 R2 R1 B+2 A+2
- // D H 0 0 A R0 R2 R1 0 0 12 A+2
- // D H 0 1 A R0 R2 R1 0 0 A+2 12
- // D H 1 1 0 0 R0 R2 R1 0 0 6 10
- // D H 1 1 0 1 R0 R2 R1 0 0 10 6
- // B 1 0 A R0 R2 R1 0 0 A+6 B+6 D=0, H=0
- // x x 1 1 1 1 1 1 1 0 0 - - Void-extent
- // x x 1 1 1 x x x x 0 0 - - Reserved*
- // x x x x x x x 0 0 0 0 - - Reserved
- // -------------------------------------------------------------------------
- //
- // D - dual plane enabled
- // H, R - used to determine the number of bits/trits/quints in texel weight encoding
- // R is a three bit value whose LSB is R0 and MSB is R1
- // Width, Height - dimensions of the texel weight grid (determined by A and B)
-
- void decodeBlockMode() {
- const int blockMode = static_cast<int>(read_astc_bits(fBlock, 0, 11));
-
- // Check for special void extent encoding
- fVoidExtent = (blockMode & 0x1FF) == 0x1FC;
-
- // Check for reserved block modes
- fError = ((blockMode & 0x1C3) == 0x1C0) || ((blockMode & 0xF) == 0);
-
- // Neither reserved nor void-extent, decode as usual
- // This code corresponds to table C.2.8 of the ASTC spec
- bool highPrecision = false;
- int R = 0;
- if ((blockMode & 0x3) == 0) {
- R = ((0xC & blockMode) >> 1) | ((0x10 & blockMode) >> 4);
- const int bitsSevenAndEight = (blockMode & 0x180) >> 7;
- SkASSERT(0 <= bitsSevenAndEight && bitsSevenAndEight < 4);
-
- const int A = (blockMode >> 5) & 0x3;
- const int B = (blockMode >> 9) & 0x3;
-
- fDualPlaneEnabled = (blockMode >> 10) & 0x1;
- highPrecision = (blockMode >> 9) & 0x1;
-
- switch (bitsSevenAndEight) {
- default:
- case 0:
- fWeightDimX = 12;
- fWeightDimY = A + 2;
- break;
-
- case 1:
- fWeightDimX = A + 2;
- fWeightDimY = 12;
- break;
-
- case 2:
- fWeightDimX = A + 6;
- fWeightDimY = B + 6;
- fDualPlaneEnabled = false;
- highPrecision = false;
- break;
-
- case 3:
- if (0 == A) {
- fWeightDimX = 6;
- fWeightDimY = 10;
- } else {
- fWeightDimX = 10;
- fWeightDimY = 6;
- }
- break;
- }
- } else { // (blockMode & 0x3) != 0
- R = ((blockMode & 0x3) << 1) | ((blockMode & 0x10) >> 4);
-
- const int bitsTwoAndThree = (blockMode >> 2) & 0x3;
- SkASSERT(0 <= bitsTwoAndThree && bitsTwoAndThree < 4);
-
- const int A = (blockMode >> 5) & 0x3;
- const int B = (blockMode >> 7) & 0x3;
-
- fDualPlaneEnabled = (blockMode >> 10) & 0x1;
- highPrecision = (blockMode >> 9) & 0x1;
-
- switch (bitsTwoAndThree) {
- case 0:
- fWeightDimX = B + 4;
- fWeightDimY = A + 2;
- break;
- case 1:
- fWeightDimX = B + 8;
- fWeightDimY = A + 2;
- break;
- case 2:
- fWeightDimX = A + 2;
- fWeightDimY = B + 8;
- break;
- case 3:
- if ((B & 0x2) == 0) {
- fWeightDimX = A + 2;
- fWeightDimY = (B & 1) + 6;
- } else {
- fWeightDimX = (B & 1) + 2;
- fWeightDimY = A + 2;
- }
- break;
- }
- }
-
- // We should have set the values of R and highPrecision
- // from decoding the block mode, these are used to determine
- // the proper dimensions of our weight grid.
- if ((R & 0x6) == 0) {
- fError = true;
- } else {
- static const int kBitAllocationTable[2][6][3] = {
- {
- { 1, 0, 0 },
- { 0, 1, 0 },
- { 2, 0, 0 },
- { 0, 0, 1 },
- { 1, 1, 0 },
- { 3, 0, 0 }
- },
- {
- { 1, 0, 1 },
- { 2, 1, 0 },
- { 4, 0, 0 },
- { 2, 0, 1 },
- { 3, 1, 0 },
- { 5, 0, 0 }
- }
- };
-
- fWeightBits = kBitAllocationTable[highPrecision][R - 2][0];
- fWeightTrits = kBitAllocationTable[highPrecision][R - 2][1];
- fWeightQuints = kBitAllocationTable[highPrecision][R - 2][2];
- }
- }
-};
-
-// Reads an ASTC block from the given pointer.
-static inline void read_astc_block(ASTCDecompressionData *dst, const uint8_t* src) {
- const uint64_t* qword = reinterpret_cast<const uint64_t*>(src);
- dst->fBlock.fLow = SkEndian_SwapLE64(qword[0]);
- dst->fBlock.fHigh = SkEndian_SwapLE64(qword[1]);
- dst->decode();
-}
-
-// Take a known void-extent block, and write out the values as a constant color.
-static void decompress_void_extent(uint8_t* dst, int dstRowBytes,
- const ASTCDecompressionData &data) {
- // The top 64 bits contain 4 16-bit RGBA values.
- int a = (static_cast<int>(read_astc_bits(data.fBlock, 112, 128)) + 255) >> 8;
- int b = (static_cast<int>(read_astc_bits(data.fBlock, 96, 112)) + 255) >> 8;
- int g = (static_cast<int>(read_astc_bits(data.fBlock, 80, 96)) + 255) >> 8;
- int r = (static_cast<int>(read_astc_bits(data.fBlock, 64, 80)) + 255) >> 8;
-
- write_constant_color(dst, data.fDimX, data.fDimY, dstRowBytes, SkColorSetARGB(a, r, g, b));
-}
-
-// Decompresses a single ASTC block. It's assumed that data.fDimX and data.fDimY are
-// set and that the block has already been decoded (i.e. data.decode() has been called)
-static void decompress_astc_block(uint8_t* dst, int dstRowBytes,
- const ASTCDecompressionData &data) {
- if (data.fError) {
- write_error_color(dst, data.fDimX, data.fDimY, dstRowBytes);
- return;
- }
-
- if (data.fVoidExtent) {
- decompress_void_extent(dst, dstRowBytes, data);
- return;
- }
-
- // According to the spec, any more than 64 values is illegal. (C.2.24)
- static const int kMaxTexelValues = 64;
-
- // Decode the texel weights.
- int texelValues[kMaxTexelValues];
- bool success = decode_integer_sequence(
- texelValues, kMaxTexelValues, data.numWeights(),
- // texel data goes to the end of the 128 bit block.
- data.fBlock, 128, 128 - data.numWeightBits(), false,
- data.fWeightBits, data.fWeightTrits, data.fWeightQuints);
-
- if (!success) {
- write_error_color(dst, data.fDimX, data.fDimY, dstRowBytes);
- return;
- }
-
- // Decode the color endpoints
- int colorBits, colorTrits, colorQuints;
- if (!data.getColorValueEncoding(&colorBits, &colorTrits, &colorQuints)) {
- write_error_color(dst, data.fDimX, data.fDimY, dstRowBytes);
- return;
- }
-
- // According to the spec, any more than 18 color values is illegal. (C.2.24)
- static const int kMaxColorValues = 18;
-
- int colorValues[kMaxColorValues];
- success = decode_integer_sequence(
- colorValues, kMaxColorValues, data.numColorValues(),
- data.fBlock, data.fColorStartBit, data.fColorEndBit, true,
- colorBits, colorTrits, colorQuints);
-
- if (!success) {
- write_error_color(dst, data.fDimX, data.fDimY, dstRowBytes);
- return;
- }
-
- // Unquantize the color values after they've been decoded.
- unquantize_colors(colorValues, data.numColorValues(), colorBits, colorTrits, colorQuints);
-
- // Decode the colors into the appropriate endpoints.
- SkColor endpoints[4][2];
- data.colorEndpoints(endpoints, colorValues);
-
- // Do texel infill and decode the texel values.
- int texelWeights[2][12][12];
- data.texelWeights(texelWeights, texelValues);
-
- // Write the texels by interpolating them based on the information
- // stored in the block.
- dst += data.fDimY * dstRowBytes;
- for (int y = 0; y < data.fDimY; ++y) {
- dst -= dstRowBytes;
- SkColor* colorPtr = reinterpret_cast<SkColor*>(dst);
- for (int x = 0; x < data.fDimX; ++x) {
- colorPtr[x] = data.getTexel(endpoints, texelWeights, x, y);
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// ASTC Comrpession Struct
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// This is the type passed as the CompressorType argument of the compressed
-// blitter for the ASTC format. The static functions required to be in this
-// struct are documented in SkTextureCompressor_Blitter.h
-struct CompressorASTC {
- static inline void CompressA8Vertical(uint8_t* dst, const uint8_t* src) {
- compress_a8_astc_block<GetAlphaTranspose>(&dst, src, 12);
- }
-
- static inline void CompressA8Horizontal(uint8_t* dst, const uint8_t* src,
- int srcRowBytes) {
- compress_a8_astc_block<GetAlpha>(&dst, src, srcRowBytes);
- }
-
-#if PEDANTIC_BLIT_RECT
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
- const uint8_t* mask) {
- // TODO: krajcevski
- // This is kind of difficult for ASTC because the weight values are calculated
- // as an average of the actual weights. The best we can do is decompress the
- // weights and recalculate them based on the new texel values. This should
- // be "not too bad" since we know that anytime we hit this function, we're
- // compressing 12x12 block dimension alpha-only, and we know the layout
- // of the block
- SkFAIL("Implement me!");
- }
-#endif
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace SkTextureCompressor {
-
-bool CompressA8To12x12ASTC(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes) {
- if (width < 0 || ((width % 12) != 0) || height < 0 || ((height % 12) != 0)) {
- return false;
- }
-
- uint8_t** dstPtr = &dst;
- for (int y = 0; y < height; y += 12) {
- for (int x = 0; x < width; x += 12) {
- compress_a8_astc_block<GetAlpha>(dstPtr, src + y*rowBytes + x, rowBytes);
- }
- }
-
- return true;
-}
-
-SkBlitter* CreateASTCBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator* allocator) {
- if ((width % 12) != 0 || (height % 12) != 0) {
- return nullptr;
- }
-
- // Memset the output buffer to an encoding that decodes to zero. We must do this
- // in order to avoid having uninitialized values in the buffer if the blitter
- // decides not to write certain scanlines (and skip entire rows of blocks).
- // In the case of ASTC, if everything index is zero, then the interpolated value
- // will decode to zero provided we have the right header. We use the encoding
- // from recognizing all zero blocks from above.
- const int nBlocks = (width * height / 144);
- uint8_t *dst = reinterpret_cast<uint8_t *>(outputBuffer);
- for (int i = 0; i < nBlocks; ++i) {
- send_packing(&dst, SkTEndian_SwapLE64(0x0000000001FE000173ULL), 0);
- }
-
- return allocator->createT<
- SkTCompressedAlphaBlitter<12, 16, CompressorASTC>>(width, height, outputBuffer);
-}
-
-void DecompressASTC(uint8_t* dst, int dstRowBytes, const uint8_t* src,
- int width, int height, int blockDimX, int blockDimY) {
- // ASTC is encoded in what they call "raster order", so that the first
- // block is the bottom-left block in the image, and the first pixel
- // is the bottom-left pixel of the image
- dst += height * dstRowBytes;
-
- ASTCDecompressionData data(blockDimX, blockDimY);
- for (int y = 0; y < height; y += blockDimY) {
- dst -= blockDimY * dstRowBytes;
- SkColor *colorPtr = reinterpret_cast<SkColor*>(dst);
- for (int x = 0; x < width; x += blockDimX) {
- read_astc_block(&data, src);
- decompress_astc_block(reinterpret_cast<uint8_t*>(colorPtr + x), dstRowBytes, data);
-
- // ASTC encoded blocks are 16 bytes (128 bits) large.
- src += 16;
- }
- }
-}
-
-} // SkTextureCompressor
diff --git a/src/utils/SkTextureCompressor_ASTC.h b/src/utils/SkTextureCompressor_ASTC.h
deleted file mode 100644
index 1312ee9c74..0000000000
--- a/src/utils/SkTextureCompressor_ASTC.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 SkTextureCompressor_ASTC_DEFINED
-#define SkTextureCompressor_ASTC_DEFINED
-
-#include "SkBitmapProcShader.h"
-
-class SkBlitter;
-
-namespace SkTextureCompressor {
-
- bool CompressA8To12x12ASTC(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes);
-
- SkBlitter* CreateASTCBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator *allocator);
-
- void DecompressASTC(uint8_t* dst, int dstRowBytes, const uint8_t* src,
- int width, int height, int blockDimX, int blockDimY);
-}
-
-#endif // SkTextureCompressor_ASTC_DEFINED
diff --git a/src/utils/SkTextureCompressor_Blitter.h b/src/utils/SkTextureCompressor_Blitter.h
deleted file mode 100644
index f488707a37..0000000000
--- a/src/utils/SkTextureCompressor_Blitter.h
+++ /dev/null
@@ -1,733 +0,0 @@
-/*
- * 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 SkTextureCompressor_Blitter_DEFINED
-#define SkTextureCompressor_Blitter_DEFINED
-
-#include "SkTypes.h"
-#include "SkBlitter.h"
-
-namespace SkTextureCompressor {
-
-// Ostensibly, SkBlitter::BlitRect is supposed to set a rect of pixels to full
-// alpha. This becomes problematic when using compressed texture blitters, since
-// the rect rarely falls along block boundaries. The proper way to handle this is
-// to update the compressed encoding of a block by resetting the proper parameters
-// (and even recompressing the block) where a rect falls inbetween block boundaries.
-// PEDANTIC_BLIT_RECT attempts to do this by requiring the struct passed to
-// SkTCompressedAlphaBlitter to implement an UpdateBlock function call.
-//
-// However, the way that BlitRect gets used almost exclusively is to bracket inverse
-// fills for paths. In other words, the top few rows and bottom few rows of a path
-// that's getting inverse filled are called using blitRect. The rest are called using
-// the standard blitAntiH. As a result, we can just call blitAntiH with a faux RLE
-// of full alpha values, and then check in our flush() call that we don't run off the
-// edge of the buffer. This is why we do not need this flag to be turned on.
-//
-// NOTE: This code is unfinished, but is inteded as a starting point if an when
-// bugs are introduced from the existing code.
-#define PEDANTIC_BLIT_RECT 0
-
-// This class implements a blitter that blits directly into a buffer that will
-// be used as an compressed alpha texture. We compute this buffer by
-// buffering scan lines and then outputting them all at once. The number of
-// scan lines buffered is controlled by kBlockSize
-//
-// The CompressorType is a struct with a bunch of static methods that provides
-// the specialized compression functionality of the blitter. A complete CompressorType
-// will implement the following static functions;
-//
-// struct CompressorType {
-// // The function used to compress an A8 block. The layout of the
-// // block is also expected to be in column-major order.
-// static void CompressA8Vertical(uint8_t* dst, const uint8_t block[]);
-//
-// // The function used to compress an A8 block. The layout of the
-// // block is also expected to be in row-major order.
-// static void CompressA8Horizontal(uint8_t* dst, const uint8_t* src, int srcRowBytes);
-//
-#if PEDANTIC_BLIT_RECT
-// // The function used to update an already compressed block. This will
-// // most likely be implementation dependent. The mask variable will have
-// // 0xFF in positions where the block should be updated and 0 in positions
-// // where it shouldn't. src contains an uncompressed buffer of pixels.
-// static void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
-// const uint8_t* mask);
-#endif
-// };
-template<int BlockDim, int EncodedBlockSize, typename CompressorType>
-class SkTCompressedAlphaBlitter : public SkBlitter {
-public:
- SkTCompressedAlphaBlitter(int width, int height, void *compressedBuffer)
- // 0x7FFE is one minus the largest positive 16-bit int. We use it for
- // debugging to make sure that we're properly setting the nextX distance
- // in flushRuns().
-#ifdef SK_DEBUG
- : fCalledOnceWithNonzeroY(false)
- , fBlitMaskCalled(false),
-#else
- :
-#endif
- kLongestRun(0x7FFE), kZeroAlpha(0)
- , fNextRun(0)
- , fWidth(width)
- , fHeight(height)
- , fBuffer(compressedBuffer)
- {
- SkASSERT((width % BlockDim) == 0);
- SkASSERT((height % BlockDim) == 0);
- }
-
- virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); }
-
- // Blit a horizontal run of one or more pixels.
- void blitH(int x, int y, int width) override {
- // This function is intended to be called from any standard RGB
- // buffer, so we should never encounter it. However, if some code
- // path does end up here, then this needs to be investigated.
- SkFAIL("Not implemented!");
- }
-
- // Blit a horizontal run of antialiased pixels; runs[] is a *sparse*
- // zero-terminated run-length encoding of spans of constant alpha values.
- void blitAntiH(int x, int y,
- const SkAlpha antialias[],
- const int16_t runs[]) override {
- SkASSERT(0 == x);
-
- // Make sure that the new row to blit is either the first
- // row that we're blitting, or it's exactly the next scan row
- // since the last row that we blit. This is to ensure that when
- // we go to flush the runs, that they are all the same four
- // runs.
- if (fNextRun > 0 &&
- ((x != fBufferedRuns[fNextRun-1].fX) ||
- (y-1 != fBufferedRuns[fNextRun-1].fY))) {
- this->flushRuns();
- }
-
- // Align the rows to a block boundary. If we receive rows that
- // are not on a block boundary, then fill in the preceding runs
- // with zeros. We do this by producing a single RLE that says
- // that we have 0x7FFE pixels of zero (0x7FFE = 32766).
- const int row = BlockDim * (y / BlockDim);
- while ((row + fNextRun) < y) {
- fBufferedRuns[fNextRun].fAlphas = &kZeroAlpha;
- fBufferedRuns[fNextRun].fRuns = &kLongestRun;
- fBufferedRuns[fNextRun].fX = 0;
- fBufferedRuns[fNextRun].fY = row + fNextRun;
- ++fNextRun;
- }
-
- // Make sure that our assumptions aren't violated...
- SkASSERT(fNextRun == (y % BlockDim));
- SkASSERT(fNextRun == 0 || fBufferedRuns[fNextRun - 1].fY < y);
-
- // Set the values of the next run
- fBufferedRuns[fNextRun].fAlphas = antialias;
- fBufferedRuns[fNextRun].fRuns = runs;
- fBufferedRuns[fNextRun].fX = x;
- fBufferedRuns[fNextRun].fY = y;
-
- // If we've output a block of scanlines in a row that don't violate our
- // assumptions, then it's time to flush them...
- if (BlockDim == ++fNextRun) {
- this->flushRuns();
- }
- }
-
- // Blit a vertical run of pixels with a constant alpha value.
- void blitV(int x, int y, int height, SkAlpha alpha) override {
- // This function is currently not implemented. It is not explicitly
- // required by the contract, but if at some time a code path runs into
- // this function (which is entirely possible), it needs to be implemented.
- //
- // TODO (krajcevski):
- // This function will be most easily implemented in one of two ways:
- // 1. Buffer each vertical column value and then construct a list
- // of alpha values and output all of the blocks at once. This only
- // requires a write to the compressed buffer
- // 2. Replace the indices of each block with the proper indices based
- // on the alpha value. This requires a read and write of the compressed
- // buffer, but much less overhead.
- SkFAIL("Not implemented!");
- }
-
- // Blit a solid rectangle one or more pixels wide. It's assumed that blitRect
- // is called as a way to bracket blitAntiH where above and below the path the
- // called path just needs a solid rectangle to fill in the mask.
-#ifdef SK_DEBUG
- bool fCalledOnceWithNonzeroY;
-#endif
- void blitRect(int x, int y, int width, int height) override {
-
- // Assumptions:
- SkASSERT(0 == x);
- SkASSERT(width <= fWidth);
-
- // Make sure that we're only ever bracketing calls to blitAntiH.
- SkASSERT((0 == y) || (!fCalledOnceWithNonzeroY && (fCalledOnceWithNonzeroY = true)));
-
-#if !(PEDANTIC_BLIT_RECT)
- for (int i = 0; i < height; ++i) {
- const SkAlpha kFullAlpha = 0xFF;
- this->blitAntiH(x, y+i, &kFullAlpha, &kLongestRun);
- }
-#else
- const int startBlockX = (x / BlockDim) * BlockDim;
- const int startBlockY = (y / BlockDim) * BlockDim;
-
- const int endBlockX = ((x + width) / BlockDim) * BlockDim;
- const int endBlockY = ((y + height) / BlockDim) * BlockDim;
-
- // If start and end are the same, then we only need to update a single block...
- if (startBlockY == endBlockY && startBlockX == endBlockX) {
- uint8_t mask[BlockDim*BlockDim];
- memset(mask, 0, sizeof(mask));
-
- const int xoff = x - startBlockX;
- SkASSERT((xoff + width) <= BlockDim);
-
- const int yoff = y - startBlockY;
- SkASSERT((yoff + height) <= BlockDim);
-
- for (int j = 0; j < height; ++j) {
- memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, width);
- }
-
- uint8_t* dst = this->getBlock(startBlockX, startBlockY);
- CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
-
- // If start and end are the same in the y dimension, then we can freely update an
- // entire row of blocks...
- } else if (startBlockY == endBlockY) {
-
- this->updateBlockRow(x, y, width, height, startBlockY, startBlockX, endBlockX);
-
- // Similarly, if the start and end are in the same column, then we can just update
- // an entire column of blocks...
- } else if (startBlockX == endBlockX) {
-
- this->updateBlockCol(x, y, width, height, startBlockX, startBlockY, endBlockY);
-
- // Otherwise, the rect spans a non-trivial region of blocks, and we have to construct
- // a kind of 9-patch to update each of the pieces of the rect. The top and bottom
- // rows are updated using updateBlockRow, and the left and right columns are updated
- // using updateBlockColumn. Anything in the middle is simply memset to an opaque block
- // encoding.
- } else {
-
- const int innerStartBlockX = startBlockX + BlockDim;
- const int innerStartBlockY = startBlockY + BlockDim;
-
- // Blit top row
- const int topRowHeight = innerStartBlockY - y;
- this->updateBlockRow(x, y, width, topRowHeight, startBlockY,
- startBlockX, endBlockX);
-
- // Advance y
- y += topRowHeight;
- height -= topRowHeight;
-
- // Blit middle
- if (endBlockY > innerStartBlockY) {
-
- // Update left row
- this->updateBlockCol(x, y, innerStartBlockX - x, endBlockY, startBlockY,
- startBlockX, innerStartBlockX);
-
- // Update the middle with an opaque encoding...
- uint8_t mask[BlockDim*BlockDim];
- memset(mask, 0xFF, sizeof(mask));
-
- uint8_t opaqueEncoding[EncodedBlockSize];
- CompressorType::CompressA8Horizontal(opaqueEncoding, mask, BlockDim);
-
- for (int j = innerStartBlockY; j < endBlockY; j += BlockDim) {
- uint8_t* opaqueDst = this->getBlock(innerStartBlockX, j);
- for (int i = innerStartBlockX; i < endBlockX; i += BlockDim) {
- memcpy(opaqueDst, opaqueEncoding, EncodedBlockSize);
- opaqueDst += EncodedBlockSize;
- }
- }
-
- // If we need to update the right column, do that too
- if (x + width > endBlockX) {
- this->updateBlockCol(endBlockX, y, x + width - endBlockX, endBlockY,
- endBlockX, innerStartBlockY, endBlockY);
- }
-
- // Advance y
- height = y + height - endBlockY;
- y = endBlockY;
- }
-
- // If we need to update the last row, then do that, too.
- if (height > 0) {
- this->updateBlockRow(x, y, width, height, endBlockY,
- startBlockX, endBlockX);
- }
- }
-#endif
- }
-
- // Blit a rectangle with one alpha-blended column on the left,
- // width (zero or more) opaque pixels, and one alpha-blended column
- // on the right. The result will always be at least two pixels wide.
- void blitAntiRect(int x, int y, int width, int height,
- SkAlpha leftAlpha, SkAlpha rightAlpha) override {
- // This function is currently not implemented. It is not explicitly
- // required by the contract, but if at some time a code path runs into
- // this function (which is entirely possible), it needs to be implemented.
- //
- // TODO (krajcevski):
- // This function will be most easily implemented as follows:
- // 1. If width/height are smaller than a block, then update the
- // indices of the affected blocks.
- // 2. If width/height are larger than a block, then construct a 9-patch
- // of block encodings that represent the rectangle, and write them
- // to the compressed buffer as necessary. Whether or not the blocks
- // are overwritten by zeros or just their indices are updated is up
- // to debate.
- SkFAIL("Not implemented!");
- }
-
- // Blit a pattern of pixels defined by a rectangle-clipped mask; We make an
- // assumption here that if this function gets called, then it will replace all
- // of the compressed texture blocks that it touches. Hence, two separate calls
- // to blitMask that have clips next to one another will cause artifacts. Most
- // of the time, however, this function gets called because constructing the mask
- // was faster than constructing the RLE for blitAntiH, and this function will
- // only be called once.
-#ifdef SK_DEBUG
- bool fBlitMaskCalled;
-#endif
- void blitMask(const SkMask& mask, const SkIRect& clip) override {
-
- // Assumptions:
- SkASSERT(!fBlitMaskCalled);
- SkDEBUGCODE(fBlitMaskCalled = true);
- SkASSERT(SkMask::kA8_Format == mask.fFormat);
- SkASSERT(mask.fBounds.contains(clip));
-
- // Start from largest block boundary less than the clip boundaries.
- const int startI = BlockDim * (clip.left() / BlockDim);
- const int startJ = BlockDim * (clip.top() / BlockDim);
-
- for (int j = startJ; j < clip.bottom(); j += BlockDim) {
-
- // Get the destination for this block row
- uint8_t* dst = this->getBlock(startI, j);
- for (int i = startI; i < clip.right(); i += BlockDim) {
-
- // At this point, the block should intersect the clip.
- SkASSERT(SkIRect::IntersectsNoEmptyCheck(
- SkIRect::MakeXYWH(i, j, BlockDim, BlockDim), clip));
-
- // Do we need to pad it?
- if (i < clip.left() || j < clip.top() ||
- i + BlockDim > clip.right() || j + BlockDim > clip.bottom()) {
-
- uint8_t block[BlockDim*BlockDim];
- memset(block, 0, sizeof(block));
-
- const int startX = SkMax32(i, clip.left());
- const int startY = SkMax32(j, clip.top());
-
- const int endX = SkMin32(i + BlockDim, clip.right());
- const int endY = SkMin32(j + BlockDim, clip.bottom());
-
- for (int y = startY; y < endY; ++y) {
- const int col = startX - i;
- const int row = y - j;
- const int valsWide = endX - startX;
- SkASSERT(valsWide <= BlockDim);
- SkASSERT(0 <= col && col < BlockDim);
- SkASSERT(0 <= row && row < BlockDim);
- memcpy(block + row*BlockDim + col,
- mask.getAddr8(startX, j + row), valsWide);
- }
-
- CompressorType::CompressA8Horizontal(dst, block, BlockDim);
- } else {
- // Otherwise, just compress it.
- uint8_t*const src = mask.getAddr8(i, j);
- const uint32_t rb = mask.fRowBytes;
- CompressorType::CompressA8Horizontal(dst, src, rb);
- }
-
- dst += EncodedBlockSize;
- }
- }
- }
-
- // If the blitter just sets a single value for each pixel, return the
- // bitmap it draws into, and assign value. If not, return nullptr and ignore
- // the value parameter.
- const SkPixmap* justAnOpaqueColor(uint32_t* value) override {
- return nullptr;
- }
-
- /**
- * Compressed texture blitters only really work correctly if they get
- * BlockDim rows at a time. That being said, this blitter tries it's best
- * to preserve semantics if blitAntiH doesn't get called in too many
- * weird ways...
- */
- int requestRowsPreserved() const override { return BlockDim; }
-
-private:
- static const int kPixelsPerBlock = BlockDim * BlockDim;
-
- // The longest possible run of pixels that this blitter will receive.
- // This is initialized in the constructor to 0x7FFE, which is one less
- // than the largest positive 16-bit integer. We make sure that it's one
- // less for debugging purposes. We also don't make this variable static
- // in order to make sure that we can construct a valid pointer to it.
- const int16_t kLongestRun;
-
- // Usually used in conjunction with kLongestRun. This is initialized to
- // zero.
- const SkAlpha kZeroAlpha;
-
- // This is the information that we buffer whenever we're asked to blit
- // a row with this blitter.
- struct BufferedRun {
- const SkAlpha* fAlphas;
- const int16_t* fRuns;
- int fX, fY;
- } fBufferedRuns[BlockDim];
-
- // The next row [0, BlockDim) that we need to blit.
- int fNextRun;
-
- // The width and height of the image that we're blitting
- const int fWidth;
- const int fHeight;
-
- // The compressed buffer that we're blitting into. It is assumed that the buffer
- // is large enough to store a compressed image of size fWidth*fHeight.
- void* const fBuffer;
-
- // Various utility functions
- int blocksWide() const { return fWidth / BlockDim; }
- int blocksTall() const { return fHeight / BlockDim; }
- int totalBlocks() const { return (fWidth * fHeight) / kPixelsPerBlock; }
-
- // Returns the block index for the block containing pixel (x, y). Block
- // indices start at zero and proceed in raster order.
- int getBlockOffset(int x, int y) const {
- SkASSERT(x < fWidth);
- SkASSERT(y < fHeight);
- const int blockCol = x / BlockDim;
- const int blockRow = y / BlockDim;
- return blockRow * this->blocksWide() + blockCol;
- }
-
- // Returns a pointer to the block containing pixel (x, y)
- uint8_t *getBlock(int x, int y) const {
- uint8_t* ptr = reinterpret_cast<uint8_t*>(fBuffer);
- return ptr + EncodedBlockSize*this->getBlockOffset(x, y);
- }
-
- // Updates the block whose columns are stored in block. curAlphai is expected
- // to store the alpha values that will be placed within each of the columns in
- // the range [col, col+colsLeft).
- typedef uint32_t Column[BlockDim/4];
- typedef uint32_t Block[BlockDim][BlockDim/4];
- inline void updateBlockColumns(Block block, const int col,
- const int colsLeft, const Column curAlphai) {
- SkASSERT(block);
- SkASSERT(col + colsLeft <= BlockDim);
-
- for (int i = col; i < (col + colsLeft); ++i) {
- memcpy(block[i], curAlphai, sizeof(Column));
- }
- }
-
- // The following function writes the buffered runs to compressed blocks.
- // If fNextRun < BlockDim, then we fill the runs that we haven't buffered with
- // the constant zero buffer.
- void flushRuns() {
- // If we don't have any runs, then just return.
- if (0 == fNextRun) {
- return;
- }
-
-#ifndef NDEBUG
- // Make sure that if we have any runs, they all match
- for (int i = 1; i < fNextRun; ++i) {
- SkASSERT(fBufferedRuns[i].fY == fBufferedRuns[i-1].fY + 1);
- SkASSERT(fBufferedRuns[i].fX == fBufferedRuns[i-1].fX);
- }
-#endif
-
- // If we don't have as many runs as we have rows, fill in the remaining
- // runs with constant zeros.
- for (int i = fNextRun; i < BlockDim; ++i) {
- fBufferedRuns[i].fY = fBufferedRuns[0].fY + i;
- fBufferedRuns[i].fX = fBufferedRuns[0].fX;
- fBufferedRuns[i].fAlphas = &kZeroAlpha;
- fBufferedRuns[i].fRuns = &kLongestRun;
- }
-
- // Make sure that our assumptions aren't violated.
- SkASSERT(fNextRun > 0 && fNextRun <= BlockDim);
- SkASSERT((fBufferedRuns[0].fY % BlockDim) == 0);
-
- // The following logic walks BlockDim rows at a time and outputs compressed
- // blocks to the buffer passed into the constructor.
- // We do the following:
- //
- // c1 c2 c3 c4
- // -----------------------------------------------------------------------
- // ... | | | | | ----> fBufferedRuns[0]
- // -----------------------------------------------------------------------
- // ... | | | | | ----> fBufferedRuns[1]
- // -----------------------------------------------------------------------
- // ... | | | | | ----> fBufferedRuns[2]
- // -----------------------------------------------------------------------
- // ... | | | | | ----> fBufferedRuns[3]
- // -----------------------------------------------------------------------
- //
- // curX -- the macro X value that we've gotten to.
- // c[BlockDim] -- the buffers that represent the columns of the current block
- // that we're operating on
- // curAlphaColumn -- buffer containing the column of alpha values from fBufferedRuns.
- // nextX -- for each run, the next point at which we need to update curAlphaColumn
- // after the value of curX.
- // finalX -- the minimum of all the nextX values.
- //
- // curX advances to finalX outputting any blocks that it passes along
- // the way. Since finalX will not change when we reach the end of a
- // run, the termination criteria will be whenever curX == finalX at the
- // end of a loop.
-
- // Setup:
- Block block;
- sk_bzero(block, sizeof(block));
-
- Column curAlphaColumn;
- sk_bzero(curAlphaColumn, sizeof(curAlphaColumn));
-
- SkAlpha *curAlpha = reinterpret_cast<SkAlpha*>(&curAlphaColumn);
-
- int nextX[BlockDim];
- for (int i = 0; i < BlockDim; ++i) {
- nextX[i] = 0x7FFFFF;
- }
-
- uint8_t* outPtr = this->getBlock(fBufferedRuns[0].fX, fBufferedRuns[0].fY);
-
- // Populate the first set of runs and figure out how far we need to
- // advance on the first step
- int curX = 0;
- int finalX = 0xFFFFF;
- for (int i = 0; i < BlockDim; ++i) {
- nextX[i] = *(fBufferedRuns[i].fRuns);
- curAlpha[i] = *(fBufferedRuns[i].fAlphas);
-
- finalX = SkMin32(nextX[i], finalX);
- }
-
- // Make sure that we have a valid right-bound X value
- SkASSERT(finalX < 0xFFFFF);
-
- // If the finalX is the longest run, then just blit until we have
- // width...
- if (kLongestRun == finalX) {
- finalX = fWidth;
- }
-
- // Run the blitter...
- while (curX != finalX) {
- SkASSERT(finalX >= curX);
-
- // Do we need to populate the rest of the block?
- if ((finalX - (BlockDim*(curX / BlockDim))) >= BlockDim) {
- const int col = curX % BlockDim;
- const int colsLeft = BlockDim - col;
- SkASSERT(curX + colsLeft <= finalX);
-
- this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
-
- // Write this block
- CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t*>(block));
- outPtr += EncodedBlockSize;
- curX += colsLeft;
- }
-
- // If we can advance even further, then just keep memsetting the block
- if ((finalX - curX) >= BlockDim) {
- SkASSERT((curX % BlockDim) == 0);
-
- const int col = 0;
- const int colsLeft = BlockDim;
-
- this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
-
- // While we can keep advancing, just keep writing the block.
- uint8_t lastBlock[EncodedBlockSize];
- CompressorType::CompressA8Vertical(lastBlock, reinterpret_cast<uint8_t*>(block));
- while((finalX - curX) >= BlockDim) {
- memcpy(outPtr, lastBlock, EncodedBlockSize);
- outPtr += EncodedBlockSize;
- curX += BlockDim;
- }
- }
-
- // If we haven't advanced within the block then do so.
- if (curX < finalX) {
- const int col = curX % BlockDim;
- const int colsLeft = finalX - curX;
-
- this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
- curX += colsLeft;
- }
-
- SkASSERT(curX == finalX);
-
- // Figure out what the next advancement is...
- if (finalX < fWidth) {
- for (int i = 0; i < BlockDim; ++i) {
- if (nextX[i] == finalX) {
- const int16_t run = *(fBufferedRuns[i].fRuns);
- fBufferedRuns[i].fRuns += run;
- fBufferedRuns[i].fAlphas += run;
- curAlpha[i] = *(fBufferedRuns[i].fAlphas);
- nextX[i] += *(fBufferedRuns[i].fRuns);
- }
- }
-
- finalX = 0xFFFFF;
- for (int i = 0; i < BlockDim; ++i) {
- finalX = SkMin32(nextX[i], finalX);
- }
- } else {
- curX = finalX;
- }
- }
-
- // If we didn't land on a block boundary, output the block...
- if ((curX % BlockDim) > 0) {
-#ifdef SK_DEBUG
- for (int i = 0; i < BlockDim; ++i) {
- SkASSERT(nextX[i] == kLongestRun || nextX[i] == curX);
- }
-#endif
- const int col = curX % BlockDim;
- const int colsLeft = BlockDim - col;
-
- memset(curAlphaColumn, 0, sizeof(curAlphaColumn));
- this->updateBlockColumns(block, col, colsLeft, curAlphaColumn);
-
- CompressorType::CompressA8Vertical(outPtr, reinterpret_cast<uint8_t*>(block));
- }
-
- fNextRun = 0;
- }
-
-#if PEDANTIC_BLIT_RECT
- void updateBlockRow(int x, int y, int width, int height,
- int blockRow, int startBlockX, int endBlockX) {
- if (0 == width || 0 == height || startBlockX == endBlockX) {
- return;
- }
-
- uint8_t* dst = this->getBlock(startBlockX, BlockDim * (y / BlockDim));
-
- // One horizontal strip to update
- uint8_t mask[BlockDim*BlockDim];
- memset(mask, 0, sizeof(mask));
-
- // Update the left cap
- int blockX = startBlockX;
- const int yoff = y - blockRow;
- for (int j = 0; j < height; ++j) {
- const int xoff = x - blockX;
- memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, BlockDim - xoff);
- }
- CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
- dst += EncodedBlockSize;
- blockX += BlockDim;
-
- // Update the middle
- if (blockX < endBlockX) {
- for (int j = 0; j < height; ++j) {
- memset(mask + (j + yoff)*BlockDim, 0xFF, BlockDim);
- }
- while (blockX < endBlockX) {
- CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
- dst += EncodedBlockSize;
- blockX += BlockDim;
- }
- }
-
- SkASSERT(endBlockX == blockX);
-
- // Update the right cap (if we need to)
- if (x + width > endBlockX) {
- memset(mask, 0, sizeof(mask));
- for (int j = 0; j < height; ++j) {
- const int xoff = (x+width-blockX);
- memset(mask + (j+yoff)*BlockDim, 0xFF, xoff);
- }
- CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
- }
- }
-
- void updateBlockCol(int x, int y, int width, int height,
- int blockCol, int startBlockY, int endBlockY) {
- if (0 == width || 0 == height || startBlockY == endBlockY) {
- return;
- }
-
- // One vertical strip to update
- uint8_t mask[BlockDim*BlockDim];
- memset(mask, 0, sizeof(mask));
- const int maskX0 = x - blockCol;
- const int maskWidth = maskX0 + width;
- SkASSERT(maskWidth <= BlockDim);
-
- // Update the top cap
- int blockY = startBlockY;
- for (int j = (y - blockY); j < BlockDim; ++j) {
- memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth);
- }
- CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), mask, BlockDim, mask);
- blockY += BlockDim;
-
- // Update middle
- if (blockY < endBlockY) {
- for (int j = 0; j < BlockDim; ++j) {
- memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth);
- }
- while (blockY < endBlockY) {
- CompressorType::UpdateBlock(this->getBlock(blockCol, blockY),
- mask, BlockDim, mask);
- blockY += BlockDim;
- }
- }
-
- SkASSERT(endBlockY == blockY);
-
- // Update bottom
- if (y + height > endBlockY) {
- for (int j = y+height; j < endBlockY + BlockDim; ++j) {
- memset(mask + (j-endBlockY)*BlockDim, 0, BlockDim);
- }
- CompressorType::UpdateBlock(this->getBlock(blockCol, blockY),
- mask, BlockDim, mask);
- }
- }
-#endif // PEDANTIC_BLIT_RECT
-
-};
-
-} // namespace SkTextureCompressor
-
-#endif // SkTextureCompressor_Blitter_DEFINED
diff --git a/src/utils/SkTextureCompressor_LATC.cpp b/src/utils/SkTextureCompressor_LATC.cpp
deleted file mode 100644
index d7cae4f37d..0000000000
--- a/src/utils/SkTextureCompressor_LATC.cpp
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTextureCompressor_LATC.h"
-#include "SkTextureCompressor_Blitter.h"
-#include "SkTextureCompressor_Utils.h"
-
-#include "SkBlitter.h"
-#include "SkEndian.h"
-
-// Compression options. In general, the slow version is much more accurate, but
-// much slower. The fast option is much faster, but much less accurate. YMMV.
-#define COMPRESS_LATC_SLOW 0
-#define COMPRESS_LATC_FAST 1
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Generates an LATC palette. LATC constructs
-// a palette of eight colors from LUM0 and LUM1 using the algorithm:
-//
-// LUM0, if lum0 > lum1 and code(x,y) == 0
-// LUM1, if lum0 > lum1 and code(x,y) == 1
-// (6*LUM0+ LUM1)/7, if lum0 > lum1 and code(x,y) == 2
-// (5*LUM0+2*LUM1)/7, if lum0 > lum1 and code(x,y) == 3
-// (4*LUM0+3*LUM1)/7, if lum0 > lum1 and code(x,y) == 4
-// (3*LUM0+4*LUM1)/7, if lum0 > lum1 and code(x,y) == 5
-// (2*LUM0+5*LUM1)/7, if lum0 > lum1 and code(x,y) == 6
-// ( LUM0+6*LUM1)/7, if lum0 > lum1 and code(x,y) == 7
-//
-// LUM0, if lum0 <= lum1 and code(x,y) == 0
-// LUM1, if lum0 <= lum1 and code(x,y) == 1
-// (4*LUM0+ LUM1)/5, if lum0 <= lum1 and code(x,y) == 2
-// (3*LUM0+2*LUM1)/5, if lum0 <= lum1 and code(x,y) == 3
-// (2*LUM0+3*LUM1)/5, if lum0 <= lum1 and code(x,y) == 4
-// ( LUM0+4*LUM1)/5, if lum0 <= lum1 and code(x,y) == 5
-// 0, if lum0 <= lum1 and code(x,y) == 6
-// 255, if lum0 <= lum1 and code(x,y) == 7
-
-static const int kLATCPaletteSize = 8;
-static void generate_latc_palette(uint8_t palette[], uint8_t lum0, uint8_t lum1) {
- palette[0] = lum0;
- palette[1] = lum1;
- if (lum0 > lum1) {
- for (int i = 1; i < 7; i++) {
- palette[i+1] = ((7-i)*lum0 + i*lum1) / 7;
- }
- } else {
- for (int i = 1; i < 5; i++) {
- palette[i+1] = ((5-i)*lum0 + i*lum1) / 5;
- }
- palette[6] = 0;
- palette[7] = 255;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-#if COMPRESS_LATC_SLOW
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Utility Functions
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// Absolute difference between two values. More correct than SkTAbs(a - b)
-// because it works on unsigned values.
-template <typename T> inline T abs_diff(const T &a, const T &b) {
- return (a > b) ? (a - b) : (b - a);
-}
-
-static bool is_extremal(uint8_t pixel) {
- return 0 == pixel || 255 == pixel;
-}
-
-typedef uint64_t (*A84x4To64BitProc)(const uint8_t block[]);
-
-// This function is used by both R11 EAC and LATC to compress 4x4 blocks
-// of 8-bit alpha into 64-bit values that comprise the compressed data.
-// For both formats, we need to make sure that the dimensions of the
-// src pixels are divisible by 4, and copy 4x4 blocks one at a time
-// for compression.
-static bool compress_4x4_a8_to_64bit(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes,
- A84x4To64BitProc proc) {
- // Make sure that our data is well-formed enough to be considered for compression
- if (0 == width || 0 == height || (width % 4) != 0 || (height % 4) != 0) {
- return false;
- }
-
- int blocksX = width >> 2;
- int blocksY = height >> 2;
-
- uint8_t block[16];
- uint64_t* encPtr = reinterpret_cast<uint64_t*>(dst);
- for (int y = 0; y < blocksY; ++y) {
- for (int x = 0; x < blocksX; ++x) {
- // Load block
- for (int k = 0; k < 4; ++k) {
- memcpy(block + k*4, src + k*rowBytes + 4*x, 4);
- }
-
- // Compress it
- *encPtr = proc(block);
- ++encPtr;
- }
- src += 4 * rowBytes;
- }
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// LATC compressor
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// LATC compressed texels down into square 4x4 blocks
-static const int kLATCBlockSize = 4;
-static const int kLATCPixelsPerBlock = kLATCBlockSize * kLATCBlockSize;
-
-// Compress a block by using the bounding box of the pixels. It is assumed that
-// there are no extremal pixels in this block otherwise we would have used
-// compressBlockBBIgnoreExtremal.
-static uint64_t compress_latc_block_bb(const uint8_t pixels[]) {
- uint8_t minVal = 255;
- uint8_t maxVal = 0;
- for (int i = 0; i < kLATCPixelsPerBlock; ++i) {
- minVal = SkTMin(pixels[i], minVal);
- maxVal = SkTMax(pixels[i], maxVal);
- }
-
- SkASSERT(!is_extremal(minVal));
- SkASSERT(!is_extremal(maxVal));
-
- uint8_t palette[kLATCPaletteSize];
- generate_latc_palette(palette, maxVal, minVal);
-
- uint64_t indices = 0;
- for (int i = kLATCPixelsPerBlock - 1; i >= 0; --i) {
-
- // Find the best palette index
- uint8_t bestError = abs_diff(pixels[i], palette[0]);
- uint8_t idx = 0;
- for (int j = 1; j < kLATCPaletteSize; ++j) {
- uint8_t error = abs_diff(pixels[i], palette[j]);
- if (error < bestError) {
- bestError = error;
- idx = j;
- }
- }
-
- indices <<= 3;
- indices |= idx;
- }
-
- return
- SkEndian_SwapLE64(
- static_cast<uint64_t>(maxVal) |
- (static_cast<uint64_t>(minVal) << 8) |
- (indices << 16));
-}
-
-// Compress a block by using the bounding box of the pixels without taking into
-// account the extremal values. The generated palette will contain extremal values
-// and fewer points along the line segment to interpolate.
-static uint64_t compress_latc_block_bb_ignore_extremal(const uint8_t pixels[]) {
- uint8_t minVal = 255;
- uint8_t maxVal = 0;
- for (int i = 0; i < kLATCPixelsPerBlock; ++i) {
- if (is_extremal(pixels[i])) {
- continue;
- }
-
- minVal = SkTMin(pixels[i], minVal);
- maxVal = SkTMax(pixels[i], maxVal);
- }
-
- SkASSERT(!is_extremal(minVal));
- SkASSERT(!is_extremal(maxVal));
-
- uint8_t palette[kLATCPaletteSize];
- generate_latc_palette(palette, minVal, maxVal);
-
- uint64_t indices = 0;
- for (int i = kLATCPixelsPerBlock - 1; i >= 0; --i) {
-
- // Find the best palette index
- uint8_t idx = 0;
- if (is_extremal(pixels[i])) {
- if (0xFF == pixels[i]) {
- idx = 7;
- } else if (0 == pixels[i]) {
- idx = 6;
- } else {
- SkFAIL("Pixel is extremal but not really?!");
- }
- } else {
- uint8_t bestError = abs_diff(pixels[i], palette[0]);
- for (int j = 1; j < kLATCPaletteSize - 2; ++j) {
- uint8_t error = abs_diff(pixels[i], palette[j]);
- if (error < bestError) {
- bestError = error;
- idx = j;
- }
- }
- }
-
- indices <<= 3;
- indices |= idx;
- }
-
- return
- SkEndian_SwapLE64(
- static_cast<uint64_t>(minVal) |
- (static_cast<uint64_t>(maxVal) << 8) |
- (indices << 16));
-}
-
-
-// Compress LATC block. Each 4x4 block of pixels is decompressed by LATC from two
-// values LUM0 and LUM1, and an index into the generated palette. Details of how
-// the palette is generated can be found in the comments of generatePalette above.
-//
-// We choose which palette type to use based on whether or not 'pixels' contains
-// any extremal values (0 or 255). If there are extremal values, then we use the
-// palette that has the extremal values built in. Otherwise, we use the full bounding
-// box.
-
-static uint64_t compress_latc_block(const uint8_t pixels[]) {
- // Collect unique pixels
- int nUniquePixels = 0;
- uint8_t uniquePixels[kLATCPixelsPerBlock];
- for (int i = 0; i < kLATCPixelsPerBlock; ++i) {
- bool foundPixel = false;
- for (int j = 0; j < nUniquePixels; ++j) {
- foundPixel = foundPixel || uniquePixels[j] == pixels[i];
- }
-
- if (!foundPixel) {
- uniquePixels[nUniquePixels] = pixels[i];
- ++nUniquePixels;
- }
- }
-
- // If there's only one unique pixel, then our compression is easy.
- if (1 == nUniquePixels) {
- return SkEndian_SwapLE64(pixels[0] | (pixels[0] << 8));
-
- // Similarly, if there are only two unique pixels, then our compression is
- // easy again: place the pixels in the block header, and assign the indices
- // with one or zero depending on which pixel they belong to.
- } else if (2 == nUniquePixels) {
- uint64_t outBlock = 0;
- for (int i = kLATCPixelsPerBlock - 1; i >= 0; --i) {
- int idx = 0;
- if (pixels[i] == uniquePixels[1]) {
- idx = 1;
- }
-
- outBlock <<= 3;
- outBlock |= idx;
- }
- outBlock <<= 16;
- outBlock |= (uniquePixels[0] | (uniquePixels[1] << 8));
- return SkEndian_SwapLE64(outBlock);
- }
-
- // Count non-maximal pixel values
- int nonExtremalPixels = 0;
- for (int i = 0; i < nUniquePixels; ++i) {
- if (!is_extremal(uniquePixels[i])) {
- ++nonExtremalPixels;
- }
- }
-
- // If all the pixels are nonmaximal then compute the palette using
- // the bounding box of all the pixels.
- if (nonExtremalPixels == nUniquePixels) {
- // This is really just for correctness, in all of my tests we
- // never take this step. We don't lose too much perf here because
- // most of the processing in this function is worth it for the
- // 1 == nUniquePixels optimization.
- return compress_latc_block_bb(pixels);
- } else {
- return compress_latc_block_bb_ignore_extremal(pixels);
- }
-}
-
-#endif // COMPRESS_LATC_SLOW
-
-////////////////////////////////////////////////////////////////////////////////
-
-#if COMPRESS_LATC_FAST
-
-// Take the top three bits of each index and pack them into the low 12
-// bits of the integer.
-static inline uint32_t pack_index(uint32_t x) {
- // Pack it in...
-#if defined (SK_CPU_BENDIAN)
- return
- (x >> 24) |
- ((x >> 13) & 0x38) |
- ((x >> 2) & 0x1C0) |
- ((x << 9) & 0xE00);
-#else
- return
- (x & 0x7) |
- ((x >> 5) & 0x38) |
- ((x >> 10) & 0x1C0) |
- ((x >> 15) & 0xE00);
-#endif
-}
-
-// Converts each 8-bit byte in the integer into an LATC index, and then packs
-// the indices into the low 12 bits of the integer.
-static inline uint32_t convert_index(uint32_t x) {
- // Since the palette is
- // 255, 0, 219, 182, 146, 109, 73, 36
- // we need to map the high three bits of each byte in the integer
- // from
- // 0 1 2 3 4 5 6 7
- // to
- // 1 7 6 5 4 3 2 0
- //
- // 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 - SkTextureCompressor::ConvertToThreeBitIndex(x);
-
- // mask is 1 if index is non-zero
- const uint32_t mask = (x | (x >> 1) | (x >> 2)) & 0x01010101;
-
- // add mask:
- // 7 6 5 4 3 2 1 0 --> 8 7 6 5 4 3 2 0
- x = (x + mask);
-
- // Handle overflow:
- // 8 7 6 5 4 3 2 0 --> 9 7 6 5 4 3 2 0
- x |= (x >> 3) & 0x01010101;
-
- // Mask out high bits:
- // 9 7 6 5 4 3 2 0 --> 1 7 6 5 4 3 2 0
- x &= 0x07070707;
-
- return pack_index(x);
-}
-
-typedef uint64_t (*PackIndicesProc)(const uint8_t* alpha, size_t rowBytes);
-template<PackIndicesProc packIndicesProc>
-static void compress_a8_latc_block(uint8_t** dstPtr, const uint8_t* src, size_t rowBytes) {
- *(reinterpret_cast<uint64_t*>(*dstPtr)) =
- SkEndian_SwapLE64(0xFF | (packIndicesProc(src, rowBytes) << 16));
- *dstPtr += 8;
-}
-
-inline uint64_t PackRowMajor(const uint8_t *indices, size_t rowBytes) {
- uint64_t result = 0;
- for (int i = 0; i < 4; ++i) {
- const uint32_t idx = *(reinterpret_cast<const uint32_t*>(indices + i*rowBytes));
- result |= static_cast<uint64_t>(convert_index(idx)) << 12*i;
- }
- return result;
-}
-
-inline uint64_t PackColumnMajor(const uint8_t *indices, size_t rowBytes) {
- // !SPEED! Blarg, this is kind of annoying. SSE4 can make this
- // a LOT faster.
- uint8_t transposed[16];
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- transposed[j*4+i] = indices[i*rowBytes + j];
- }
- }
-
- return PackRowMajor(transposed, 4);
-}
-
-static bool compress_4x4_a8_latc(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes) {
-
- if (width < 0 || ((width % 4) != 0) || height < 0 || ((height % 4) != 0)) {
- return false;
- }
-
- uint8_t** dstPtr = &dst;
- for (int y = 0; y < height; y += 4) {
- for (int x = 0; x < width; x += 4) {
- compress_a8_latc_block<PackRowMajor>(dstPtr, src + y*rowBytes + x, rowBytes);
- }
- }
-
- return true;
-}
-
-void CompressA8LATCBlockVertical(uint8_t* dst, const uint8_t block[]) {
- compress_a8_latc_block<PackColumnMajor>(&dst, block, 4);
-}
-
-#endif // COMPRESS_LATC_FAST
-
-void decompress_latc_block(uint8_t* dst, int dstRowBytes, const uint8_t* src) {
- uint64_t block = SkEndian_SwapLE64(*(reinterpret_cast<const uint64_t *>(src)));
- uint8_t lum0 = block & 0xFF;
- uint8_t lum1 = (block >> 8) & 0xFF;
-
- uint8_t palette[kLATCPaletteSize];
- generate_latc_palette(palette, lum0, lum1);
-
- block >>= 16;
- for (int j = 0; j < 4; ++j) {
- for (int i = 0; i < 4; ++i) {
- dst[i] = palette[block & 0x7];
- block >>= 3;
- }
- dst += dstRowBytes;
- }
-}
-
-// This is the type passed as the CompressorType argument of the compressed
-// blitter for the LATC format. The static functions required to be in this
-// struct are documented in SkTextureCompressor_Blitter.h
-struct CompressorLATC {
- static inline void CompressA8Vertical(uint8_t* dst, const uint8_t block[]) {
- compress_a8_latc_block<PackColumnMajor>(&dst, block, 4);
- }
-
- static inline void CompressA8Horizontal(uint8_t* dst, const uint8_t* src,
- int srcRowBytes) {
- compress_a8_latc_block<PackRowMajor>(&dst, src, srcRowBytes);
- }
-
-#if PEDANTIC_BLIT_RECT
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
- const uint8_t* mask) {
- // Pack the mask
- uint64_t cmpMask = 0;
- for (int i = 0; i < 4; ++i) {
- const uint32_t idx = *(reinterpret_cast<const uint32_t*>(src + i*srcRowBytes));
- cmpMask |= static_cast<uint64_t>(pack_index(idx)) << 12*i;
- }
- cmpMask = SkEndian_SwapLE64(cmpMask << 16); // avoid header
-
- uint64_t cmpSrc;
- uint8_t *cmpSrcPtr = reinterpret_cast<uint8_t*>(&cmpSrc);
- compress_a8_latc_block<PackRowMajor>(&cmpSrcPtr, src, srcRowBytes);
-
- // Mask out header
- cmpSrc = cmpSrc & cmpMask;
-
- // Read destination encoding
- uint64_t *cmpDst = reinterpret_cast<uint64_t*>(dst);
-
- // If the destination is the encoding for a blank block, then we need
- // to properly set the header
- if (0 == cmpDst) {
- *cmpDst = SkTEndian_SwapLE64(0x24924924924900FFULL);
- }
-
- // Set the new indices
- *cmpDst &= ~cmpMask;
- *cmpDst |= cmpSrc;
- }
-#endif // PEDANTIC_BLIT_RECT
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace SkTextureCompressor {
-
-bool CompressA8ToLATC(uint8_t* dst, const uint8_t* src, int width, int height, size_t rowBytes) {
-#if COMPRESS_LATC_FAST
- return compress_4x4_a8_latc(dst, src, width, height, rowBytes);
-#elif COMPRESS_LATC_SLOW
- return compress_4x4_a8_to_64bit(dst, src, width, height, rowBytes, compress_latc_block);
-#else
-#error "Must choose either fast or slow LATC compression"
-#endif
-}
-
-SkBlitter* CreateLATCBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator* allocator) {
- if ((width % 4) != 0 || (height % 4) != 0) {
- return nullptr;
- }
-
-#if COMPRESS_LATC_FAST
- // Memset the output buffer to an encoding that decodes to zero. We must do this
- // in order to avoid having uninitialized values in the buffer if the blitter
- // decides not to write certain scanlines (and skip entire rows of blocks).
- // In the case of LATC, if everything is zero, then LUM0 and LUM1 are also zero,
- // and they will only be non-zero (0xFF) if the index is 7. So bzero will do just fine.
- // (8 bytes per block) * (w * h / 16 blocks) = w * h / 2
- sk_bzero(outputBuffer, width * height / 2);
-
- return allocator->createT<
- SkTCompressedAlphaBlitter<4, 8, CompressorLATC>>(width, height, outputBuffer);
-#elif COMPRESS_LATC_SLOW
- // TODO (krajcevski)
- return nullptr;
-#endif
-}
-
-void DecompressLATC(uint8_t* dst, int dstRowBytes, const uint8_t* src, int width, int height) {
- for (int j = 0; j < height; j += 4) {
- for (int i = 0; i < width; i += 4) {
- decompress_latc_block(dst + i, dstRowBytes, src);
- src += 8;
- }
- dst += 4 * dstRowBytes;
- }
-}
-
-} // SkTextureCompressor
diff --git a/src/utils/SkTextureCompressor_LATC.h b/src/utils/SkTextureCompressor_LATC.h
deleted file mode 100644
index 85647eafcc..0000000000
--- a/src/utils/SkTextureCompressor_LATC.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 SkTextureCompressor_LATC_DEFINED
-#define SkTextureCompressor_LATC_DEFINED
-
-#include "SkBitmapProcShader.h"
-
-class SkBlitter;
-
-namespace SkTextureCompressor {
-
- bool CompressA8ToLATC(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes);
-
- SkBlitter* CreateLATCBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator *allocator);
-
- void DecompressLATC(uint8_t* dst, int dstRowBytes, const uint8_t* src, int width, int height);
-}
-
-#endif // SkTextureCompressor_LATC_DEFINED
diff --git a/src/utils/SkTextureCompressor_R11EAC.cpp b/src/utils/SkTextureCompressor_R11EAC.cpp
deleted file mode 100644
index 5bdfead58c..0000000000
--- a/src/utils/SkTextureCompressor_R11EAC.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkTextureCompressor.h"
-#include "SkTextureCompressor_Blitter.h"
-#include "SkTextureCompressor_Utils.h"
-
-#include "SkBlitter.h"
-#include "SkEndian.h"
-
-// #define COMPRESS_R11_EAC_SLOW 1
-// #define COMPRESS_R11_EAC_FAST 1
-#define COMPRESS_R11_EAC_FASTEST 1
-
-// Blocks compressed into R11 EAC are represented as follows:
-// 0000000000000000000000000000000000000000000000000000000000000000
-// |base_cw|mod|mul| ----------------- indices -------------------
-//
-// To reconstruct the value of a given pixel, we use the formula:
-// clamp[0, 2047](base_cw * 8 + 4 + mod_val*mul*8)
-//
-// mod_val is chosen from a palette of values based on the index of the
-// given pixel. The palette is chosen by the value stored in mod.
-// This formula returns a value between 0 and 2047, which is converted
-// to a float from 0 to 1 in OpenGL.
-//
-// If mul is zero, then we set mul = 1/8, so that the formula becomes
-// clamp[0, 2047](base_cw * 8 + 4 + mod_val)
-
-static const int kNumR11EACPalettes = 16;
-static const int kR11EACPaletteSize = 8;
-static const int kR11EACModifierPalettes[kNumR11EACPalettes][kR11EACPaletteSize] = {
- {-3, -6, -9, -15, 2, 5, 8, 14},
- {-3, -7, -10, -13, 2, 6, 9, 12},
- {-2, -5, -8, -13, 1, 4, 7, 12},
- {-2, -4, -6, -13, 1, 3, 5, 12},
- {-3, -6, -8, -12, 2, 5, 7, 11},
- {-3, -7, -9, -11, 2, 6, 8, 10},
- {-4, -7, -8, -11, 3, 6, 7, 10},
- {-3, -5, -8, -11, 2, 4, 7, 10},
- {-2, -6, -8, -10, 1, 5, 7, 9},
- {-2, -5, -8, -10, 1, 4, 7, 9},
- {-2, -4, -8, -10, 1, 3, 7, 9},
- {-2, -5, -7, -10, 1, 4, 6, 9},
- {-3, -4, -7, -10, 2, 3, 6, 9},
- {-1, -2, -3, -10, 0, 1, 2, 9},
- {-4, -6, -8, -9, 3, 5, 7, 8},
- {-3, -5, -7, -9, 2, 4, 6, 8}
-};
-
-#if COMPRESS_R11_EAC_SLOW
-
-// Pack the base codeword, palette, and multiplier into the 64 bits necessary
-// to decode it.
-static uint64_t pack_r11eac_block(uint16_t base_cw, uint16_t palette, uint16_t multiplier,
- uint64_t indices) {
- SkASSERT(palette < 16);
- SkASSERT(multiplier < 16);
- SkASSERT(indices < (static_cast<uint64_t>(1) << 48));
-
- const uint64_t b = static_cast<uint64_t>(base_cw) << 56;
- const uint64_t m = static_cast<uint64_t>(multiplier) << 52;
- const uint64_t p = static_cast<uint64_t>(palette) << 48;
- return SkEndian_SwapBE64(b | m | p | indices);
-}
-
-// Given a base codeword, a modifier, and a multiplier, compute the proper
-// pixel value in the range [0, 2047].
-static uint16_t compute_r11eac_pixel(int base_cw, int modifier, int multiplier) {
- int ret = (base_cw * 8 + 4) + (modifier * multiplier * 8);
- return (ret > 2047)? 2047 : ((ret < 0)? 0 : ret);
-}
-
-// Compress a block into R11 EAC format.
-// The compression works as follows:
-// 1. Find the center of the span of the block's values. Use this as the base codeword.
-// 2. Choose a multiplier based roughly on the size of the span of block values
-// 3. Iterate through each palette and choose the one with the most accurate
-// modifiers.
-static inline uint64_t compress_heterogeneous_r11eac_block(const uint8_t block[16]) {
- // Find the center of the data...
- uint16_t bmin = block[0];
- uint16_t bmax = block[0];
- for (int i = 1; i < 16; ++i) {
- bmin = SkTMin<uint16_t>(bmin, block[i]);
- bmax = SkTMax<uint16_t>(bmax, block[i]);
- }
-
- uint16_t center = (bmax + bmin) >> 1;
- SkASSERT(center <= 255);
-
- // Based on the min and max, we can guesstimate a proper multiplier
- // This is kind of a magic choice to start with.
- uint16_t multiplier = (bmax - center) / 10;
-
- // Now convert the block to 11 bits and transpose it to match
- // the proper layout
- uint16_t cblock[16];
- for (int i = 0; i < 4; ++i) {
- for (int j = 0; j < 4; ++j) {
- int srcIdx = i*4+j;
- int dstIdx = j*4+i;
- cblock[dstIdx] = (block[srcIdx] << 3) | (block[srcIdx] >> 5);
- }
- }
-
- // Finally, choose the proper palette and indices
- uint32_t bestError = 0xFFFFFFFF;
- uint64_t bestIndices = 0;
- uint16_t bestPalette = 0;
- for (uint16_t paletteIdx = 0; paletteIdx < kNumR11EACPalettes; ++paletteIdx) {
- const int *palette = kR11EACModifierPalettes[paletteIdx];
-
- // Iterate through each pixel to find the best palette index
- // and update the indices with the choice. Also store the error
- // for this palette to be compared against the best error...
- uint32_t error = 0;
- uint64_t indices = 0;
- for (int pixelIdx = 0; pixelIdx < 16; ++pixelIdx) {
- const uint16_t pixel = cblock[pixelIdx];
-
- // Iterate through each palette value to find the best index
- // for this particular pixel for this particular palette.
- uint16_t bestPixelError =
- abs_diff(pixel, compute_r11eac_pixel(center, palette[0], multiplier));
- int bestIndex = 0;
- for (int i = 1; i < kR11EACPaletteSize; ++i) {
- const uint16_t p = compute_r11eac_pixel(center, palette[i], multiplier);
- const uint16_t perror = abs_diff(pixel, p);
-
- // Is this index better?
- if (perror < bestPixelError) {
- bestIndex = i;
- bestPixelError = perror;
- }
- }
-
- SkASSERT(bestIndex < 8);
-
- error += bestPixelError;
- indices <<= 3;
- indices |= bestIndex;
- }
-
- SkASSERT(indices < (static_cast<uint64_t>(1) << 48));
-
- // Is this palette better?
- if (error < bestError) {
- bestPalette = paletteIdx;
- bestIndices = indices;
- bestError = error;
- }
- }
-
- // Finally, pack everything together...
- return pack_r11eac_block(center, bestPalette, multiplier, bestIndices);
-}
-#endif // COMPRESS_R11_EAC_SLOW
-
-#if COMPRESS_R11_EAC_FAST
-// This function takes into account that most blocks that we compress have a gradation from
-// fully opaque to fully transparent. The compression scheme works by selecting the
-// palette and multiplier that has the tightest fit to the 0-255 range. This is encoded
-// as the block header (0x8490). The indices are then selected by considering the top
-// three bits of each alpha value. For alpha masks, this reduces the dynamic range from
-// 17 to 8, but the quality is still acceptable.
-//
-// There are a few caveats that need to be taken care of...
-//
-// 1. The block is read in as scanlines, so the indices are stored as:
-// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-// However, the decomrpession routine reads them in column-major order, so they
-// need to be packed as:
-// 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15
-// So when reading, they must be transposed.
-//
-// 2. We cannot use the top three bits as an index directly, since the R11 EAC palettes
-// above store the modulation values first decreasing and then increasing:
-// e.g. {-3, -6, -9, -15, 2, 5, 8, 14}
-// Hence, we need to convert the indices with the following mapping:
-// From: 0 1 2 3 4 5 6 7
-// To: 3 2 1 0 4 5 6 7
-static inline uint64_t compress_heterogeneous_r11eac_block(const uint8_t block[16]) {
- uint64_t retVal = static_cast<uint64_t>(0x8490) << 48;
- for(int i = 0; i < 4; ++i) {
- for(int j = 0; j < 4; ++j) {
- const int shift = 45-3*(j*4+i);
- SkASSERT(shift <= 45);
- const uint64_t idx = block[i*4+j] >> 5;
- SkASSERT(idx < 8);
-
- // !SPEED! This is slightly faster than having an if-statement.
- switch(idx) {
- case 0:
- case 1:
- case 2:
- case 3:
- retVal |= (3-idx) << shift;
- break;
- default:
- retVal |= idx << shift;
- break;
- }
- }
- }
-
- return SkEndian_SwapBE64(retVal);
-}
-#endif // COMPRESS_R11_EAC_FAST
-
-#if (COMPRESS_R11_EAC_SLOW) || (COMPRESS_R11_EAC_FAST)
-static uint64_t compress_r11eac_block(const uint8_t block[16]) {
- // Are all blocks a solid color?
- bool solid = true;
- for (int i = 1; i < 16; ++i) {
- if (block[i] != block[0]) {
- solid = false;
- break;
- }
- }
-
- if (solid) {
- switch(block[0]) {
- // Fully transparent? We know the encoding...
- case 0:
- // (0x0020 << 48) produces the following:
- // basw_cw: 0
- // mod: 0, palette: {-3, -6, -9, -15, 2, 5, 8, 14}
- // multiplier: 2
- // mod_val: -3
- //
- // this gives the following formula:
- // clamp[0, 2047](0*8+4+(-3)*2*8) = 0
- //
- // Furthermore, it is impervious to endianness:
- // 0x0020000000002000ULL
- // Will produce one pixel with index 2, which gives:
- // clamp[0, 2047](0*8+4+(-9)*2*8) = 0
- return 0x0020000000002000ULL;
-
- // Fully opaque? We know this encoding too...
- case 255:
-
- // -1 produces the following:
- // basw_cw: 255
- // mod: 15, palette: {-3, -5, -7, -9, 2, 4, 6, 8}
- // mod_val: 8
- //
- // this gives the following formula:
- // clamp[0, 2047](255*8+4+8*8*8) = clamp[0, 2047](2556) = 2047
- return 0xFFFFFFFFFFFFFFFFULL;
-
- default:
- // !TODO! krajcevski:
- // This will probably never happen, since we're using this format
- // primarily for compressing alpha maps. Usually the only
- // non-fullly opaque or fully transparent blocks are not a solid
- // intermediate color. If we notice that they are, then we can
- // add another optimization...
- break;
- }
- }
-
- return compress_heterogeneous_r11eac_block(block);
-}
-
-// This function is used by R11 EAC to compress 4x4 blocks
-// of 8-bit alpha into 64-bit values that comprise the compressed data.
-// We need to make sure that the dimensions of the src pixels are divisible
-// by 4, and copy 4x4 blocks one at a time for compression.
-typedef uint64_t (*A84x4To64BitProc)(const uint8_t block[]);
-
-static bool compress_4x4_a8_to_64bit(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes,
- A84x4To64BitProc proc) {
- // Make sure that our data is well-formed enough to be considered for compression
- if (0 == width || 0 == height || (width % 4) != 0 || (height % 4) != 0) {
- return false;
- }
-
- int blocksX = width >> 2;
- int blocksY = height >> 2;
-
- uint8_t block[16];
- uint64_t* encPtr = reinterpret_cast<uint64_t*>(dst);
- for (int y = 0; y < blocksY; ++y) {
- for (int x = 0; x < blocksX; ++x) {
- // Load block
- for (int k = 0; k < 4; ++k) {
- memcpy(block + k*4, src + k*rowBytes + 4*x, 4);
- }
-
- // Compress it
- *encPtr = proc(block);
- ++encPtr;
- }
- src += 4 * rowBytes;
- }
-
- return true;
-}
-#endif // (COMPRESS_R11_EAC_SLOW) || (COMPRESS_R11_EAC_FAST)
-
-// This function converts an integer containing four bytes of alpha
-// values into an integer containing four bytes of indices into R11 EAC.
-// Note, there needs to be a mapping of indices:
-// 0 1 2 3 4 5 6 7
-// 3 2 1 0 4 5 6 7
-//
-// To compute this, we first negate each byte, and then add three, which
-// gives the mapping
-// 3 2 1 0 -1 -2 -3 -4
-//
-// Then we mask out the negative values, take their absolute value, and
-// add three.
-//
-// 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 = SkTextureCompressor::ConvertToThreeBitIndex(x);
-
- // Negate...
- x = ~((0x80808080 - x) ^ 0x7F7F7F7F);
-
- // Add three
- const uint32_t s = (x & 0x7F7F7F7F) + 0x03030303;
- x = ((x ^ 0x03030303) & 0x80808080) ^ s;
-
- // Absolute value
- const uint32_t a = x & 0x80808080;
- const uint32_t b = a >> 7;
-
- // Aside: mask negatives (m is three if the byte was negative)
- const uint32_t m = (a >> 6) | b;
-
- // .. continue absolute value
- x = (x ^ ((a - b) | a)) + b;
-
- // Add three
- return x + m;
-}
-
-#if COMPRESS_R11_EAC_FASTEST
-template<unsigned shift>
-static inline uint64_t swap_shift(uint64_t x, uint64_t mask) {
- const uint64_t t = (x ^ (x >> shift)) & mask;
- return x ^ t ^ (t << shift);
-}
-
-static inline uint64_t interleave6(uint64_t topRows, uint64_t bottomRows) {
- // If our 3-bit block indices are laid out as:
- // a b c d
- // e f g h
- // i j k l
- // m n o p
- //
- // This function expects topRows and bottomRows to contain the first two rows
- // of indices interleaved in the least significant bits of a and b. In other words...
- //
- // If the architecture is big endian, then topRows and bottomRows will contain the following:
- // Bits 31-0:
- // a: 00 a e 00 b f 00 c g 00 d h
- // b: 00 i m 00 j n 00 k o 00 l p
- //
- // If the architecture is little endian, then topRows and bottomRows will contain
- // the following:
- // Bits 31-0:
- // a: 00 d h 00 c g 00 b f 00 a e
- // b: 00 l p 00 k o 00 j n 00 i m
- //
- // This function returns a 48-bit packing of the form:
- // a e i m b f j n c g k o d h l p
- //
- // !SPEED! this function might be even faster if certain SIMD intrinsics are
- // used..
-
- // For both architectures, we can figure out a packing of the bits by
- // using a shuffle and a few shift-rotates...
- uint64_t x = (static_cast<uint64_t>(topRows) << 32) | static_cast<uint64_t>(bottomRows);
-
- // x: 00 a e 00 b f 00 c g 00 d h 00 i m 00 j n 00 k o 00 l p
-
- x = swap_shift<10>(x, 0x3FC0003FC00000ULL);
-
- // x: b f 00 00 00 a e c g i m 00 00 00 d h j n 00 k o 00 l p
-
- x = (x | ((x << 52) & (0x3FULL << 52)) | ((x << 20) & (0x3FULL << 28))) >> 16;
-
- // x: 00 00 00 00 00 00 00 00 b f l p a e c g i m k o d h j n
-
- x = swap_shift<6>(x, 0xFC0000ULL);
-
-#if defined (SK_CPU_BENDIAN)
- // x: 00 00 00 00 00 00 00 00 b f l p a e i m c g k o d h j n
-
- x = swap_shift<36>(x, 0x3FULL);
-
- // x: 00 00 00 00 00 00 00 00 b f j n a e i m c g k o d h l p
-
- x = swap_shift<12>(x, 0xFFF000000ULL);
-#else
- // If our CPU is little endian, then the above logic will
- // produce the following indices:
- // x: 00 00 00 00 00 00 00 00 c g i m d h l p b f j n a e k o
-
- x = swap_shift<36>(x, 0xFC0ULL);
-
- // x: 00 00 00 00 00 00 00 00 a e i m d h l p b f j n c g k o
-
- x = (x & (0xFFFULL << 36)) | ((x & 0xFFFFFFULL) << 12) | ((x >> 24) & 0xFFFULL);
-#endif
-
- // x: 00 00 00 00 00 00 00 00 a e i m b f j n c g k o d h l p
- return x;
-}
-
-// This function follows the same basic procedure as compress_heterogeneous_r11eac_block
-// above when COMPRESS_R11_EAC_FAST is defined, but it avoids a few loads/stores and
-// tries to optimize where it can using SIMD.
-static uint64_t compress_r11eac_block_fast(const uint8_t* src, size_t rowBytes) {
- // Store each row of alpha values in an integer
- const uint32_t alphaRow1 = *(reinterpret_cast<const uint32_t*>(src));
- const uint32_t alphaRow2 = *(reinterpret_cast<const uint32_t*>(src + rowBytes));
- const uint32_t alphaRow3 = *(reinterpret_cast<const uint32_t*>(src + 2*rowBytes));
- const uint32_t alphaRow4 = *(reinterpret_cast<const uint32_t*>(src + 3*rowBytes));
-
- // Check for solid blocks. The explanations for these values
- // can be found in the comments of compress_r11eac_block above
- if (alphaRow1 == alphaRow2 && alphaRow1 == alphaRow3 && alphaRow1 == alphaRow4) {
- if (0 == alphaRow1) {
- // Fully transparent block
- return 0x0020000000002000ULL;
- } else if (0xFFFFFFFF == alphaRow1) {
- // Fully opaque block
- return 0xFFFFFFFFFFFFFFFFULL;
- }
- }
-
- // Convert each integer of alpha values into an integer of indices
- const uint32_t indexRow1 = convert_indices(alphaRow1);
- const uint32_t indexRow2 = convert_indices(alphaRow2);
- const uint32_t indexRow3 = convert_indices(alphaRow3);
- const uint32_t indexRow4 = convert_indices(alphaRow4);
-
- // Interleave the indices from the top two rows and bottom two rows
- // prior to passing them to interleave6. Since each index is at most
- // three bits, then each byte can hold two indices... The way that the
- // compression scheme expects the packing allows us to efficiently pack
- // the top two rows and bottom two rows. Interleaving each 6-bit sequence
- // and tightly packing it into a uint64_t is a little trickier, which is
- // taken care of in interleave6.
- const uint32_t r1r2 = (indexRow1 << 3) | indexRow2;
- const uint32_t r3r4 = (indexRow3 << 3) | indexRow4;
- const uint64_t indices = interleave6(r1r2, r3r4);
-
- // Return the packed incdices in the least significant bits with the magic header
- return SkEndian_SwapBE64(0x8490000000000000ULL | indices);
-}
-
-static bool compress_a8_to_r11eac_fast(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes) {
- // Make sure that our data is well-formed enough to be considered for compression
- if (0 == width || 0 == height || (width % 4) != 0 || (height % 4) != 0) {
- return false;
- }
-
- const int blocksX = width >> 2;
- const int blocksY = height >> 2;
-
- uint64_t* encPtr = reinterpret_cast<uint64_t*>(dst);
- for (int y = 0; y < blocksY; ++y) {
- for (int x = 0; x < blocksX; ++x) {
- // Compress it
- *encPtr = compress_r11eac_block_fast(src + 4*x, rowBytes);
- ++encPtr;
- }
- src += 4 * rowBytes;
- }
- return true;
-}
-#endif // COMPRESS_R11_EAC_FASTEST
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Utility functions used by the blitter
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// The R11 EAC format expects that indices are given in column-major order. Since
-// we receive alpha values in raster order, this usually means that we have to use
-// pack6 above to properly pack our indices. However, if our indices come from the
-// blitter, then each integer will be a column of indices, and hence can be efficiently
-// packed. This function takes the bottom three bits of each byte and places them in
-// the least significant 12 bits of the resulting integer.
-static inline uint32_t pack_indices_vertical(uint32_t x) {
-#if defined (SK_CPU_BENDIAN)
- return
- (x & 7) |
- ((x >> 5) & (7 << 3)) |
- ((x >> 10) & (7 << 6)) |
- ((x >> 15) & (7 << 9));
-#else
- return
- ((x >> 24) & 7) |
- ((x >> 13) & (7 << 3)) |
- ((x >> 2) & (7 << 6)) |
- ((x << 9) & (7 << 9));
-#endif
-}
-
-// This function returns the compressed format of a block given as four columns of
-// alpha values. Each column is assumed to be loaded from top to bottom, and hence
-// must first be converted to indices and then packed into the resulting 64-bit
-// integer.
-inline void compress_block_vertical(uint8_t* dstPtr, const uint8_t *block) {
-
- const uint32_t* src = reinterpret_cast<const uint32_t*>(block);
- uint64_t* dst = reinterpret_cast<uint64_t*>(dstPtr);
-
- const uint32_t alphaColumn0 = src[0];
- const uint32_t alphaColumn1 = src[1];
- const uint32_t alphaColumn2 = src[2];
- const uint32_t alphaColumn3 = src[3];
-
- if (alphaColumn0 == alphaColumn1 &&
- alphaColumn2 == alphaColumn3 &&
- alphaColumn0 == alphaColumn2) {
-
- if (0 == alphaColumn0) {
- // Transparent
- *dst = 0x0020000000002000ULL;
- return;
- }
- else if (0xFFFFFFFF == alphaColumn0) {
- // Opaque
- *dst = 0xFFFFFFFFFFFFFFFFULL;
- return;
- }
- }
-
- const uint32_t indexColumn0 = convert_indices(alphaColumn0);
- const uint32_t indexColumn1 = convert_indices(alphaColumn1);
- const uint32_t indexColumn2 = convert_indices(alphaColumn2);
- const uint32_t indexColumn3 = convert_indices(alphaColumn3);
-
- const uint32_t packedIndexColumn0 = pack_indices_vertical(indexColumn0);
- const uint32_t packedIndexColumn1 = pack_indices_vertical(indexColumn1);
- const uint32_t packedIndexColumn2 = pack_indices_vertical(indexColumn2);
- const uint32_t packedIndexColumn3 = pack_indices_vertical(indexColumn3);
-
- *dst = SkEndian_SwapBE64(0x8490000000000000ULL |
- (static_cast<uint64_t>(packedIndexColumn0) << 36) |
- (static_cast<uint64_t>(packedIndexColumn1) << 24) |
- static_cast<uint64_t>(packedIndexColumn2 << 12) |
- static_cast<uint64_t>(packedIndexColumn3));
-}
-
-static inline int get_r11_eac_index(uint64_t block, int x, int y) {
- SkASSERT(x >= 0 && x < 4);
- SkASSERT(y >= 0 && y < 4);
- const int idx = x*4 + y;
- return (block >> ((15-idx)*3)) & 0x7;
-}
-
-static void decompress_r11_eac_block(uint8_t* dst, int dstRowBytes, const uint8_t* src) {
- const uint64_t block = SkEndian_SwapBE64(*(reinterpret_cast<const uint64_t *>(src)));
-
- const int base_cw = (block >> 56) & 0xFF;
- const int mod = (block >> 52) & 0xF;
- const int palette_idx = (block >> 48) & 0xF;
-
- const int* palette = kR11EACModifierPalettes[palette_idx];
-
- for (int j = 0; j < 4; ++j) {
- for (int i = 0; i < 4; ++i) {
- const int idx = get_r11_eac_index(block, i, j);
- const int val = base_cw*8 + 4 + palette[idx]*mod*8;
- if (val < 0) {
- dst[i] = 0;
- } else if (val > 2047) {
- dst[i] = 0xFF;
- } else {
- dst[i] = (val >> 3) & 0xFF;
- }
- }
- dst += dstRowBytes;
- }
-}
-
-// This is the type passed as the CompressorType argument of the compressed
-// blitter for the R11 EAC format. The static functions required to be in this
-// struct are documented in SkTextureCompressor_Blitter.h
-struct CompressorR11EAC {
- static inline void CompressA8Vertical(uint8_t* dst, const uint8_t* src) {
- compress_block_vertical(dst, src);
- }
-
- static inline void CompressA8Horizontal(uint8_t* dst, const uint8_t* src,
- int srcRowBytes) {
- *(reinterpret_cast<uint64_t*>(dst)) = compress_r11eac_block_fast(src, srcRowBytes);
- }
-
-#if PEDANTIC_BLIT_RECT
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
- const uint8_t* mask) {
- // TODO: krajcevski
- // The implementation of this function should be similar to that of LATC, since
- // the R11EAC indices directly correspond to pixel values.
- SkFAIL("Implement me!");
- }
-#endif
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace SkTextureCompressor {
-
-bool CompressA8ToR11EAC(uint8_t* dst, const uint8_t* src, int width, int height, size_t rowBytes) {
-
-#if (COMPRESS_R11_EAC_SLOW) || (COMPRESS_R11_EAC_FAST)
-
- return compress_4x4_a8_to_64bit(dst, src, width, height, rowBytes, compress_r11eac_block);
-
-#elif COMPRESS_R11_EAC_FASTEST
-
- return compress_a8_to_r11eac_fast(dst, src, width, height, rowBytes);
-
-#else
-#error "Must choose R11 EAC algorithm"
-#endif
-}
-
-SkBlitter* CreateR11EACBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator* allocator) {
-
- if ((width % 4) != 0 || (height % 4) != 0) {
- return nullptr;
- }
-
- // Memset the output buffer to an encoding that decodes to zero. We must do this
- // in order to avoid having uninitialized values in the buffer if the blitter
- // decides not to write certain scanlines (and skip entire rows of blocks).
- // In the case of R11, we use the encoding from recognizing all zero pixels from above.
- const int nBlocks = (width * height / 16); // 4x4 pixel blocks.
- uint64_t *dst = reinterpret_cast<uint64_t *>(outputBuffer);
- for (int i = 0; i < nBlocks; ++i) {
- *dst = 0x0020000000002000ULL;
- ++dst;
- }
-
- return allocator->createT<
- SkTCompressedAlphaBlitter<4, 8, CompressorR11EAC>>(width, height, outputBuffer);
-}
-
-void DecompressR11EAC(uint8_t* dst, int dstRowBytes, const uint8_t* src, int width, int height) {
- for (int j = 0; j < height; j += 4) {
- for (int i = 0; i < width; i += 4) {
- decompress_r11_eac_block(dst + i, dstRowBytes, src);
- src += 8;
- }
- dst += 4 * dstRowBytes;
- }
-}
-
-} // namespace SkTextureCompressor
diff --git a/src/utils/SkTextureCompressor_R11EAC.h b/src/utils/SkTextureCompressor_R11EAC.h
deleted file mode 100644
index abaabfb363..0000000000
--- a/src/utils/SkTextureCompressor_R11EAC.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 SkTextureCompressor_R11EAC_DEFINED
-#define SkTextureCompressor_R11EAC_DEFINED
-
-#include "SkBitmapProcShader.h"
-
-class SkBlitter;
-
-namespace SkTextureCompressor {
-
- bool CompressA8ToR11EAC(uint8_t* dst, const uint8_t* src,
- int width, int height, size_t rowBytes);
-
- SkBlitter* CreateR11EACBlitter(int width, int height, void* outputBuffer,
- SkTBlitterAllocator* allocator);
-
- void DecompressR11EAC(uint8_t* dst, int dstRB, const uint8_t* src, int width, int height);
-}
-
-#endif // SkTextureCompressor_R11EAC_DEFINED
diff --git a/src/utils/SkTextureCompressor_Utils.h b/src/utils/SkTextureCompressor_Utils.h
deleted file mode 100644
index 6fb0d6b190..0000000000
--- a/src/utils/SkTextureCompressor_Utils.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-* 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
-
-#include <cstdint>
-
-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
deleted file mode 100644
index 06be9cefec..0000000000
--- a/tests/TextureCompressionTest.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkAutoPixmapStorage.h"
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkEndian.h"
-#include "SkImageInfo.h"
-#include "SkTemplates.h"
-#include "SkTextureCompressor.h"
-#include "Test.h"
-
-// TODO: Create separate tests for RGB and RGBA data once
-// ASTC and ETC1 decompression is implemented.
-
-static bool decompresses_a8(SkTextureCompressor::Format fmt) {
- switch (fmt) {
- case SkTextureCompressor::kLATC_Format:
- case SkTextureCompressor::kR11_EAC_Format:
- return true;
-
- default:
- return false;
- }
-}
-
-static bool compresses_a8(SkTextureCompressor::Format fmt) {
- switch (fmt) {
- case SkTextureCompressor::kLATC_Format:
- case SkTextureCompressor::kR11_EAC_Format:
- case SkTextureCompressor::kASTC_12x12_Format:
- return true;
-
- default:
- return false;
- }
-}
-
-/**
- * Make sure that we properly fail when we don't have multiple of four image dimensions.
- */
-DEF_TEST(CompressAlphaFailDimensions, reporter) {
- static const int kWidth = 17;
- static const int kHeight = 17;
-
- // R11_EAC and LATC are both dimensions of 4, so we need to make sure that we
- // are violating those assumptions. And if we are, then we're also violating the
- // assumptions of ASTC, which is 12x12 since any number not divisible by 4 is
- // also not divisible by 12. Our dimensions are prime, so any block dimension
- // larger than 1 should fail.
- REPORTER_ASSERT(reporter, kWidth % 4 != 0);
- REPORTER_ASSERT(reporter, kHeight % 4 != 0);
-
- SkAutoPixmapStorage pixmap;
- pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight));
- // leaving the pixels uninitialized, as they don't affect the test...
-
- for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
- const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
- if (!compresses_a8(fmt)) {
- continue;
- }
- sk_sp<SkData> data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt));
- REPORTER_ASSERT(reporter, nullptr == data);
- }
-}
-
-/**
- * Make sure that we properly fail when we don't have the correct bitmap type.
- * compressed textures can (currently) only be created from A8 bitmaps.
- */
-DEF_TEST(CompressAlphaFailColorType, reporter) {
- static const int kWidth = 12;
- static const int kHeight = 12;
-
- // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible
- // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might
- // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would
- // need to be updated.
- REPORTER_ASSERT(reporter, kWidth % 12 == 0);
- REPORTER_ASSERT(reporter, kHeight % 12 == 0);
-
- SkAutoPixmapStorage pixmap;
- pixmap.alloc(SkImageInfo::MakeN32Premul(kWidth, kHeight));
- // leaving the pixels uninitialized, as they don't affect the test...
-
- for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
- const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
- if (!compresses_a8(fmt)) {
- continue;
- }
- sk_sp<SkData> data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt));
- REPORTER_ASSERT(reporter, nullptr == data);
- }
-}
-
-/**
- * Make sure that if you compress a texture with alternating black/white pixels, and
- * then decompress it, you get what you started with.
- */
-DEF_TEST(CompressCheckerboard, reporter) {
- static const int kWidth = 48; // We need the number to be divisible by both
- static const int kHeight = 48; // 12 (ASTC) and 16 (ARM NEON R11 EAC).
-
- // ASTC is at most 12x12, and any dimension divisible by 12 is also divisible
- // by 4, which is the dimensions of R11_EAC and LATC. In the future, we might
- // support additional variants of ASTC, such as 5x6 and 8x8, in which case this would
- // need to be updated. Additionally, ARM NEON and SSE code paths support up to
- // four blocks of R11 EAC at once, so they operate on 16-wide blocks. Hence, the
- // valid width and height is going to be the LCM of 12 and 16 which is 4*4*3 = 48
- REPORTER_ASSERT(reporter, kWidth % 48 == 0);
- REPORTER_ASSERT(reporter, kHeight % 48 == 0);
-
- SkAutoPixmapStorage pixmap;
- pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight));
-
- // Populate the pixels
- {
- uint8_t* pixels = reinterpret_cast<uint8_t*>(pixmap.writable_addr());
- REPORTER_ASSERT(reporter, pixels);
- if (nullptr == pixels) {
- return;
- }
-
- for (int y = 0; y < kHeight; ++y) {
- for (int x = 0; x < kWidth; ++x) {
- if ((x ^ y) & 1) {
- pixels[x] = 0xFF;
- } else {
- pixels[x] = 0;
- }
- }
- pixels += pixmap.rowBytes();
- }
- }
-
- SkAutoTMalloc<uint8_t> decompMemory(kWidth*kHeight);
- uint8_t* decompBuffer = decompMemory.get();
- REPORTER_ASSERT(reporter, decompBuffer);
- if (nullptr == decompBuffer) {
- return;
- }
-
- for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) {
- const SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i);
-
- // Ignore formats for RGBA data, since the decompressed buffer
- // won't match the size and contents of the original.
- if (!decompresses_a8(fmt) || !compresses_a8(fmt)) {
- continue;
- }
-
- sk_sp<SkData> data(SkTextureCompressor::CompressBitmapToFormat(pixmap, fmt));
- REPORTER_ASSERT(reporter, data);
- if (nullptr == data) {
- continue;
- }
-
- bool decompResult =
- SkTextureCompressor::DecompressBufferFromFormat(
- decompBuffer, kWidth,
- data->bytes(),
- kWidth, kHeight, fmt);
- REPORTER_ASSERT(reporter, decompResult);
-
- const uint8_t* pixels = reinterpret_cast<const uint8_t*>(pixmap.addr());
- REPORTER_ASSERT(reporter, pixels);
- if (nullptr == pixels) {
- continue;
- }
-
- for (int y = 0; y < kHeight; ++y) {
- for (int x = 0; x < kWidth; ++x) {
- bool ok = pixels[y*pixmap.rowBytes() + x] == decompBuffer[y*kWidth + x];
- REPORTER_ASSERT(reporter, ok);
- }
- }
- }
-}
-
-/**
- * Make sure that if we pass in a solid color bitmap that we get the appropriate results
- */
-DEF_TEST(CompressLATC, reporter) {
-
- const SkTextureCompressor::Format kLATCFormat = SkTextureCompressor::kLATC_Format;
- static const int kLATCEncodedBlockSize = 8;
-
- static const int kWidth = 8;
- static const int kHeight = 8;
-
- SkAutoPixmapStorage pixmap;
- pixmap.alloc(SkImageInfo::MakeA8(kWidth, kHeight));
-
- int latcDimX, latcDimY;
- SkTextureCompressor::GetBlockDimensions(kLATCFormat, &latcDimX, &latcDimY);
-
- REPORTER_ASSERT(reporter, kWidth % latcDimX == 0);
- REPORTER_ASSERT(reporter, kHeight % latcDimY == 0);
- const size_t kSizeToBe =
- SkTextureCompressor::GetCompressedDataSize(kLATCFormat, kWidth, kHeight);
- REPORTER_ASSERT(reporter, kSizeToBe == ((kWidth*kHeight*kLATCEncodedBlockSize)/16));
- REPORTER_ASSERT(reporter, (kSizeToBe % kLATCEncodedBlockSize) == 0);
-
- for (int lum = 0; lum < 256; ++lum) {
- uint8_t* pixels = reinterpret_cast<uint8_t*>(pixmap.writable_addr());
- for (int i = 0; i < kWidth*kHeight; ++i) {
- pixels[i] = lum;
- }
-
- sk_sp<SkData> latcData(
- SkTextureCompressor::CompressBitmapToFormat(pixmap, kLATCFormat));
- REPORTER_ASSERT(reporter, latcData);
- if (nullptr == latcData) {
- continue;
- }
-
- REPORTER_ASSERT(reporter, kSizeToBe == latcData->size());
-
- // Make sure that it all matches a given block encoding. Since we have
- // COMPRESS_LATC_FAST defined in SkTextureCompressor_LATC.cpp, we are using
- // an approximation scheme that optimizes for speed against coverage maps.
- // That means that each palette in the encoded block is exactly the same,
- // 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 };
-
- // 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 |
- (kIndex << 16) | (kIndex << 19) | (kIndex << 22) | (kIndex << 25) |
- (kIndex << 28) | (kIndex << 31) | (kIndex << 34) | (kIndex << 37) |
- (kIndex << 40) | (kIndex << 43) | (kIndex << 46) | (kIndex << 49) |
- (kIndex << 52) | (kIndex << 55) | (kIndex << 58) | (kIndex << 61));
-
- const uint64_t* blockPtr = reinterpret_cast<const uint64_t*>(latcData->data());
- for (size_t i = 0; i < (kSizeToBe/8); ++i) {
- REPORTER_ASSERT(reporter, blockPtr[i] == kConstColorEncoding);
- }
- }
-}