aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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);
- }
- }
-}