Chromium Code Reviews| 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..f8b5346207fec8ccfc89f02fc8cc0bc32104f6f9 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,52 @@ 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. |
|
robertphillips
2014/07/21 21:56:57
both formats ?
krajcevski
2014/07/21 22:03:55
Done.
|
| +// 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. |
| +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 +343,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 +351,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 +482,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 +551,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 +568,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; |
| - } |
| +bool CompressA8ToR11EAC(uint8_t* dst, const uint8_t* src, int width, int height, int rowBytes) { |
| - default: |
| - SkFAIL("Unknown compressed format!"); |
| - return 0; |
| - } |
| -} |
| - |
| -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 +860,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 +895,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]); |
|
robertphillips
2014/07/21 21:56:57
Why not pass the pointer into compress_block_verti
krajcevski
2014/07/21 22:03:55
I tried that, it seemed like it was a tad slower,
|
| ++outPtr; |
| curX += colsLeft; |
| } |
| @@ -1081,10 +910,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 +926,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 +952,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 |