| Index: src/utils/SkTextureCompressor_R11EAC.cpp
|
| diff --git a/src/utils/SkTextureCompressor.cpp b/src/utils/SkTextureCompressor_R11EAC.cpp
|
| similarity index 69%
|
| copy from src/utils/SkTextureCompressor.cpp
|
| copy to src/utils/SkTextureCompressor_R11EAC.cpp
|
| index a593b36880e8588fe50a0eb0bfcea68afac2b320..3ce0120ec5fa29e6c4577896d1dd3d2d677b533c 100644
|
| --- a/src/utils/SkTextureCompressor.cpp
|
| +++ b/src/utils/SkTextureCompressor_R11EAC.cpp
|
| @@ -7,292 +7,8 @@
|
|
|
| #include "SkTextureCompressor.h"
|
|
|
| -#include "SkBitmap.h"
|
| -#include "SkData.h"
|
| #include "SkEndian.h"
|
|
|
| -#include "SkTextureCompression_opts.h"
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -//
|
| -// 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, int 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 kLATCPaletteSize = 8;
|
| -static const int kLATCBlockSize = 4;
|
| -static const int kLATCPixelsPerBlock = kLATCBlockSize * kLATCBlockSize;
|
| -
|
| -// 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 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;
|
| - }
|
| -}
|
| -
|
| -// 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);
|
| - }
|
| -}
|
| -
|
| -static inline bool compress_a8_to_latc(uint8_t* dst, const uint8_t* src,
|
| - int width, int height, int rowBytes) {
|
| - return compress_4x4_a8_to_64bit(dst, src, width, height, rowBytes, compress_latc_block);
|
| -}
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -//
|
| -// R11 EAC Compressor
|
| -//
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -
|
| // #define COMPRESS_R11_EAC_SLOW 1
|
| // #define COMPRESS_R11_EAC_FAST 1
|
| #define COMPRESS_R11_EAC_FASTEST 1
|
| @@ -548,9 +264,51 @@ static uint64_t compress_r11eac_block(const uint8_t block[16]) {
|
|
|
| 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, int 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)
|
|
|
| #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
|
| @@ -584,8 +342,7 @@ static inline uint64_t interleave6(uint64_t topRows, 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
|
|
|
| - uint64_t t = (x ^ (x >> 10)) & 0x3FC0003FC00000ULL;
|
| - x = x ^ t ^ (t << 10);
|
| + 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
|
|
|
| @@ -593,38 +350,30 @@ static inline uint64_t interleave6(uint64_t topRows, uint64_t bottomRows) {
|
|
|
| // 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
|
|
|
| - t = (x ^ (x >> 6)) & 0xFC0000ULL;
|
| - x = x ^ t ^ (t << 6);
|
| + 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
|
|
|
| - t = (x ^ (x >> 36)) & 0x3FULL;
|
| - x = x ^ t ^ (t << 36);
|
| + 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
|
|
|
| - t = (x ^ (x >> 12)) & 0xFFF000000ULL;
|
| - x = x ^ t ^ (t << 12);
|
| -
|
| - // 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;
|
| + 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
|
|
|
| - t = (x ^ (x >> 36)) & 0xFC0ULL;
|
| - x = x ^ t ^ (t << 36);
|
| + 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;
|
| -#endif
|
| }
|
|
|
| // This function converts an integer containing four bytes of alpha
|
| @@ -732,6 +481,12 @@ static bool compress_a8_to_r11eac_fast(uint8_t* dst, const uint8_t* src,
|
| }
|
| #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
|
| @@ -795,43 +550,16 @@ static inline uint64_t compress_block_vertical(const uint32_t alphaColumn0,
|
|
|
| }
|
|
|
| -static inline bool compress_a8_to_r11eac(uint8_t* dst, const uint8_t* src,
|
| - int width, int height, int 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
|
| -}
|
| -
|
| // Updates the block whose columns are stored in blockColN. curAlphai is expected
|
| // to store, as an integer, the four alpha values that will be placed within each
|
| // of the columns in the range [col, col+colsLeft).
|
| -static inline void update_block_columns(
|
| - uint32_t* blockCol1, uint32_t* blockCol2, uint32_t* blockCol3, uint32_t* blockCol4,
|
| - const int col, const int colsLeft, const uint32_t curAlphai) {
|
| - SkASSERT(NULL != blockCol1);
|
| - SkASSERT(NULL != blockCol2);
|
| - SkASSERT(NULL != blockCol3);
|
| - SkASSERT(NULL != blockCol4);
|
| +static inline void update_block_columns(uint32_t* block, const int col,
|
| + const int colsLeft, const uint32_t curAlphai) {
|
| + SkASSERT(NULL != block);
|
| SkASSERT(col + colsLeft <= 4);
|
| +
|
| for (int i = col; i < (col + colsLeft); ++i) {
|
| - switch(i) {
|
| - case 0:
|
| - *blockCol1 = curAlphai;
|
| - break;
|
| - case 1:
|
| - *blockCol2 = curAlphai;
|
| - break;
|
| - case 2:
|
| - *blockCol3 = curAlphai;
|
| - break;
|
| - case 3:
|
| - *blockCol4 = curAlphai;
|
| - break;
|
| - }
|
| + block[i] = curAlphai;
|
| }
|
| }
|
|
|
| @@ -839,78 +567,182 @@ static inline void update_block_columns(
|
|
|
| namespace SkTextureCompressor {
|
|
|
| -static inline size_t get_compressed_data_size(Format fmt, int width, int height) {
|
| - switch (fmt) {
|
| - // These formats are 64 bits per 4x4 block.
|
| - case kR11_EAC_Format:
|
| - case kLATC_Format:
|
| - {
|
| - static const int kLATCEncodedBlockSize = 8;
|
| -
|
| - const int blocksX = width / kLATCBlockSize;
|
| - const int blocksY = height / kLATCBlockSize;
|
| -
|
| - return blocksX * blocksY * kLATCEncodedBlockSize;
|
| - }
|
| -
|
| - default:
|
| - SkFAIL("Unknown compressed format!");
|
| - return 0;
|
| - }
|
| -}
|
| +bool CompressA8ToR11EAC(uint8_t* dst, const uint8_t* src, int width, int height, int rowBytes) {
|
|
|
| -bool CompressBufferToFormat(uint8_t* dst, const uint8_t* src, SkColorType srcColorType,
|
| - int width, int height, int rowBytes, Format format, bool opt) {
|
| - CompressionProc proc = NULL;
|
| - if (opt) {
|
| - proc = SkTextureCompressorGetPlatformProc(srcColorType, format);
|
| - }
|
| +#if (COMPRESS_R11_EAC_SLOW) || (COMPRESS_R11_EAC_FAST)
|
|
|
| - if (NULL == proc) {
|
| - switch (srcColorType) {
|
| - case kAlpha_8_SkColorType:
|
| - {
|
| - switch (format) {
|
| - case kLATC_Format:
|
| - proc = compress_a8_to_latc;
|
| - break;
|
| - case kR11_EAC_Format:
|
| - proc = compress_a8_to_r11eac;
|
| - break;
|
| - default:
|
| - // Do nothing...
|
| - break;
|
| - }
|
| - }
|
| - break;
|
| + return compress_4x4_a8_to_64bit(dst, src, width, height, rowBytes, compress_r11eac_block);
|
|
|
| - default:
|
| - // Do nothing...
|
| - break;
|
| - }
|
| - }
|
| +#elif COMPRESS_R11_EAC_FASTEST
|
|
|
| - if (NULL != proc) {
|
| - return proc(dst, src, width, height, rowBytes);
|
| - }
|
| + return compress_a8_to_r11eac_fast(dst, src, width, height, rowBytes);
|
|
|
| - return false;
|
| +#else
|
| +#error "Must choose R11 EAC algorithm"
|
| +#endif
|
| }
|
|
|
| -SkData *CompressBitmapToFormat(const SkBitmap &bitmap, Format format) {
|
| - SkAutoLockPixels alp(bitmap);
|
| -
|
| - int compressedDataSize = get_compressed_data_size(format, bitmap.width(), bitmap.height());
|
| - const uint8_t* src = reinterpret_cast<const uint8_t*>(bitmap.getPixels());
|
| - uint8_t* dst = reinterpret_cast<uint8_t*>(sk_malloc_throw(compressedDataSize));
|
| - if (CompressBufferToFormat(dst, src, bitmap.colorType(), bitmap.width(), bitmap.height(),
|
| - bitmap.rowBytes(), format)) {
|
| - return SkData::NewFromMalloc(dst, compressedDataSize);
|
| +// This class implements a blitter that blits directly into a buffer that will
|
| +// be used as an R11 EAC compressed texture. We compute this buffer by
|
| +// buffering four scan lines and then outputting them all at once. This blitter
|
| +// is only expected to be used with alpha masks, i.e. kAlpha8_SkColorType.
|
| +class R11_EACBlitter : public SkBlitter {
|
| +public:
|
| + R11_EACBlitter(int width, int height, void *compressedBuffer);
|
| + virtual ~R11_EACBlitter() { this->flushRuns(); }
|
| +
|
| + // Blit a horizontal run of one or more pixels.
|
| + virtual void blitH(int x, int y, int width) SK_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.
|
| + virtual void blitAntiH(int x, int y,
|
| + const SkAlpha antialias[],
|
| + const int16_t runs[]) SK_OVERRIDE;
|
| +
|
| + // Blit a vertical run of pixels with a constant alpha value.
|
| + virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_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.
|
| + virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
|
| + // Analogous to blitRow, this function is intended for RGB targets
|
| + // and should never be called by this blitter. Any calls to this function
|
| + // are probably a bug and should be investigated.
|
| + SkFAIL("Not implemented!");
|
| + }
|
| +
|
| + // 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.
|
| + virtual void blitAntiRect(int x, int y, int width, int height,
|
| + SkAlpha leftAlpha, SkAlpha rightAlpha) SK_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;
|
| + // typically used for text.
|
| + virtual void blitMask(const SkMask&, const SkIRect& clip) SK_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 the same way as
|
| + // blitAntiRect above.
|
| + SkFAIL("Not implemented!");
|
| + }
|
| +
|
| + // If the blitter just sets a single value for each pixel, return the
|
| + // bitmap it draws into, and assign value. If not, return NULL and ignore
|
| + // the value parameter.
|
| + virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE {
|
| + return NULL;
|
| + }
|
| +
|
| + /**
|
| + * Compressed texture blitters only really work correctly if they get
|
| + * four blocks 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...
|
| + */
|
| + virtual int requestRowsPreserved() const { return kR11_EACBlockSz; }
|
| +
|
| +protected:
|
| + virtual void onNotifyFinished() { this->flushRuns(); }
|
| +
|
| +private:
|
| + static const int kR11_EACBlockSz = 4;
|
| + static const int kPixelsPerBlock = kR11_EACBlockSz * kR11_EACBlockSz;
|
| +
|
| + // 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[kR11_EACBlockSz];
|
| +
|
| + // The next row (0-3) that we need to blit. This value should never exceed
|
| + // the number of rows that we have (kR11_EACBlockSz)
|
| + int fNextRun;
|
| +
|
| + // The width and height of the image that we're blitting
|
| + const int fWidth;
|
| + const int fHeight;
|
| +
|
| + // The R11 EAC buffer that we're blitting into. It is assumed that the buffer
|
| + // is large enough to store a compressed image of size fWidth*fHeight.
|
| + uint64_t* const fBuffer;
|
| +
|
| + // Various utility functions
|
| + int blocksWide() const { return fWidth / kR11_EACBlockSz; }
|
| + int blocksTall() const { return fHeight / kR11_EACBlockSz; }
|
| + 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 / kR11_EACBlockSz;
|
| + const int blockRow = y / kR11_EACBlockSz;
|
| + return blockRow * this->blocksWide() + blockCol;
|
| + }
|
| +
|
| + // Returns a pointer to the block containing pixel (x, y)
|
| + uint64_t *getBlock(int x, int y) const {
|
| + return fBuffer + this->getBlockOffset(x, y);
|
| + }
|
| +
|
| + // The following function writes the buffered runs to compressed blocks.
|
| + // If fNextRun < 4, then we fill the runs that we haven't buffered with
|
| + // the constant zero buffer.
|
| + void flushRuns();
|
| +};
|
|
|
| - sk_free(dst);
|
| - return NULL;
|
| -}
|
|
|
| R11_EACBlitter::R11_EACBlitter(int width, int height, void *latcBuffer)
|
| // 0x7FFE is one minus the largest positive 16-bit int. We use it for
|
| @@ -1027,11 +859,7 @@ void R11_EACBlitter::flushRuns() {
|
| // end of a loop.
|
|
|
| // Setup:
|
| - uint32_t c1 = 0;
|
| - uint32_t c2 = 0;
|
| - uint32_t c3 = 0;
|
| - uint32_t c4 = 0;
|
| -
|
| + uint32_t c[4] = { 0, 0, 0, 0 };
|
| uint32_t curAlphaColumn = 0;
|
| SkAlpha *curAlpha = reinterpret_cast<SkAlpha*>(&curAlphaColumn);
|
|
|
| @@ -1066,10 +894,10 @@ void R11_EACBlitter::flushRuns() {
|
| const int colsLeft = 4 - col;
|
| SkASSERT(curX + colsLeft <= finalX);
|
|
|
| - update_block_columns(&c1, &c2, &c3, &c4, col, colsLeft, curAlphaColumn);
|
| + update_block_columns(c, col, colsLeft, curAlphaColumn);
|
|
|
| // Write this block
|
| - *outPtr = compress_block_vertical(c1, c2, c3, c4);
|
| + *outPtr = compress_block_vertical(c[0], c[1], c[2], c[3]);
|
| ++outPtr;
|
| curX += colsLeft;
|
| }
|
| @@ -1081,10 +909,10 @@ void R11_EACBlitter::flushRuns() {
|
| const int col = 0;
|
| const int colsLeft = kR11_EACBlockSz;
|
|
|
| - update_block_columns(&c1, &c2, &c3, &c4, col, colsLeft, curAlphaColumn);
|
| + update_block_columns(c, col, colsLeft, curAlphaColumn);
|
|
|
| // While we can keep advancing, just keep writing the block.
|
| - uint64_t lastBlock = compress_block_vertical(c1, c2, c3, c4);
|
| + uint64_t lastBlock = compress_block_vertical(c[0], c[1], c[2], c[3]);
|
| while((finalX - curX) >= kR11_EACBlockSz) {
|
| *outPtr = lastBlock;
|
| ++outPtr;
|
| @@ -1097,7 +925,7 @@ void R11_EACBlitter::flushRuns() {
|
| const int col = curX & 3;
|
| const int colsLeft = finalX - curX;
|
|
|
| - update_block_columns(&c1, &c2, &c3, &c4, col, colsLeft, curAlphaColumn);
|
| + update_block_columns(c, col, colsLeft, curAlphaColumn);
|
|
|
| curX += colsLeft;
|
| }
|
| @@ -1123,10 +951,14 @@ void R11_EACBlitter::flushRuns() {
|
|
|
| // If we didn't land on a block boundary, output the block...
|
| if ((curX & 3) > 1) {
|
| - *outPtr = compress_block_vertical(c1, c2, c3, c4);
|
| + *outPtr = compress_block_vertical(c[0], c[1], c[2], c[3]);
|
| }
|
|
|
| fNextRun = 0;
|
| }
|
|
|
| +SkBlitter* CreateR11EACBlitter(int width, int height, void* outputBuffer) {
|
| + return new R11_EACBlitter(width, height, outputBuffer);
|
| +}
|
| +
|
| } // namespace SkTextureCompressor
|
|
|