diff options
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | gn/utils.gni | 10 | ||||
-rw-r--r-- | src/core/SkOpts.cpp | 4 | ||||
-rw-r--r-- | src/core/SkOpts.h | 6 | ||||
-rw-r--r-- | src/core/SkOverdrawCanvas.cpp | 1 | ||||
-rw-r--r-- | src/opts/SkTextureCompressor_opts.h | 266 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor.cpp | 231 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor.h | 110 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_ASTC.cpp | 2100 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_ASTC.h | 27 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_Blitter.h | 733 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_LATC.cpp | 518 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_LATC.h | 26 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_R11EAC.cpp | 669 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_R11EAC.h | 26 | ||||
-rw-r--r-- | src/utils/SkTextureCompressor_Utils.h | 70 | ||||
-rw-r--r-- | tests/TextureCompressionTest.cpp | 277 |
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); - } - } -} |