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 |