Chromium Code Reviews| Index: src/utils/SkTextureCompressor.cpp |
| diff --git a/src/utils/SkTextureCompressor.cpp b/src/utils/SkTextureCompressor.cpp |
| index 2b33a13469582fcb7a0f29d21b33725d238f9269..2bbee6c0f4d564b49d785eab7d53491f0581faa8 100644 |
| --- a/src/utils/SkTextureCompressor.cpp |
| +++ b/src/utils/SkTextureCompressor.cpp |
| @@ -732,6 +732,69 @@ static bool compress_a8_to_r11eac_fast(uint8_t* dst, const uint8_t* src, |
| } |
| #endif // COMPRESS_R11_EAC_FASTEST |
| +// 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. |
| +static inline uint64_t compress_block_vertical(const uint32_t alphaColumn0, |
| + const uint32_t alphaColumn1, |
| + const uint32_t alphaColumn2, |
| + const uint32_t alphaColumn3) { |
| + |
| + if (alphaColumn0 == alphaColumn1 && |
| + alphaColumn2 == alphaColumn3 && |
| + alphaColumn0 == alphaColumn2) { |
| + |
| + if (0 == alphaColumn0) { |
| + // Transparent |
| + return 0x0020000000002000ULL; |
| + } |
| + else if (0xFFFFFFFF == alphaColumn0) { |
| + // Opaque |
| + return 0xFFFFFFFFFFFFFFFFULL; |
| + } |
| + } |
| + |
| + 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); |
| + |
| + return 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 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) |
| @@ -820,4 +883,195 @@ SkData *CompressBitmapToFormat(const SkBitmap &bitmap, Format format) { |
| return NULL; |
| } |
| +R11_EACBlitter::R11_EACBlitter(int width, int height, void *latcBuffer) |
|
robertphillips
2014/07/21 14:28:06
Where is 0x7FFE coming from?
krajcevski
2014/07/21 17:35:26
Done.
|
| + : kLongestRun(0x7FFE), kZeroAlpha(0) |
| + , fNextRun(0) |
| + , fWidth(width) |
| + , fHeight(height) |
| + , fBuffer(reinterpret_cast<uint64_t*const>(latcBuffer)) |
| +{ |
| + SkASSERT((width % kR11_EACBlockSz) == 0); |
| + SkASSERT((height % kR11_EACBlockSz) == 0); |
| +} |
| + |
| +void R11_EACBlitter::blitAntiH(int x, int y, |
| + const SkAlpha* antialias, |
| + const int16_t* runs) SK_OVERRIDE { |
| + if (fNextRun > 0 && |
| + ((x != fBufferedRuns[fNextRun-1].fX) || |
| + (y-1 != fBufferedRuns[fNextRun-1].fY))) { |
|
robertphillips
2014/07/21 14:28:06
// comment ?
krajcevski
2014/07/21 17:35:26
Done.
|
| + this->flushRuns(); |
| + } |
| + |
| + const int row = y & ~3; |
| + while ((row + fNextRun) < y) { |
|
robertphillips
2014/07/21 14:28:06
// comment?
krajcevski
2014/07/21 17:35:26
Done.
|
| + fBufferedRuns[fNextRun].fAlphas = &kZeroAlpha; |
| + fBufferedRuns[fNextRun].fRuns = &kLongestRun; |
| + fBufferedRuns[fNextRun].fX = 0; |
| + fBufferedRuns[fNextRun].fY = row + fNextRun; |
| + ++fNextRun; |
| + } |
| + |
| + SkASSERT(fNextRun == (y & 3)); |
| + SkASSERT(fNextRun == 0 || fBufferedRuns[fNextRun - 1].fY < y); |
| + |
| + fBufferedRuns[fNextRun].fAlphas = antialias; |
| + fBufferedRuns[fNextRun].fRuns = runs; |
| + fBufferedRuns[fNextRun].fX = x; |
| + fBufferedRuns[fNextRun].fY = y; |
| + |
| + if (4 == ++fNextRun) { |
| + this->flushRuns(); |
| + } |
| +} |
| + |
| +void R11_EACBlitter::flushRuns() { |
| + |
| + if (0 == fNextRun) { |
| + return; |
| + } |
| + |
|
robertphillips
2014/07/21 14:28:06
left justify # lines ?
krajcevski
2014/07/21 17:35:26
Done.
|
| + #ifndef NDEBUG |
| + for (int i = 1; i < fNextRun; ++i) { |
| + SkASSERT(fBufferedRuns[i].fY == fBufferedRuns[i-1].fY + 1); |
| + } |
| + #endif |
| + |
| + for (int i = fNextRun; i < kR11_EACBlockSz; ++i) { |
| + fBufferedRuns[i].fY = fBufferedRuns[0].fY + i; |
| + fBufferedRuns[i].fX = fBufferedRuns[0].fX; |
| + fBufferedRuns[i].fAlphas = &kZeroAlpha; |
| + fBufferedRuns[i].fRuns = &kLongestRun; |
| + } |
| + |
| + SkASSERT(fNextRun > 0 && fNextRun <= 4); |
| + SkASSERT((fBufferedRuns[0].fY & 3) == 0); |
| + |
| + // Setup |
| + uint32_t blockCol1 = 0; |
| + uint32_t blockCol2 = 0; |
| + uint32_t blockCol3 = 0; |
| + uint32_t blockCol4 = 0; |
| + |
| + uint32_t curAlphai = 0; |
| + SkAlpha *curAlpha = reinterpret_cast<SkAlpha*>(&curAlphai); |
| + |
| + int nextX[kR11_EACBlockSz]; |
| + for (int i = 0; i < kR11_EACBlockSz; ++i) { |
| + nextX[i] = 0x7FFFFF; |
| + } |
| + |
| + uint64_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 leg |
| + int curX = 0; |
| + int finalX = 0xFFFFF; |
| + for (int i = 0; i < kR11_EACBlockSz; ++i) { |
| + nextX[i] = *(fBufferedRuns[i].fRuns); |
| + curAlpha[i] = *(fBufferedRuns[i].fAlphas); |
| + |
| + finalX = SkMin32(nextX[i], finalX); |
| + } |
| + |
|
robertphillips
2014/07/21 14:28:06
Would making these two inlined functions kill perf
krajcevski
2014/07/21 17:35:26
Done.
|
| + #define UPDATE_BLOCK do { \ |
| + 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; \ |
| + } \ |
| + } \ |
| + } while(0) |
| + |
| + #define COMPRESS_BLOCK \ |
| + compress_block_vertical(blockCol1, blockCol2, blockCol3, blockCol4) |
| + |
| + // Run the blitter... |
| + while (curX != finalX) { |
| + SkASSERT(finalX >= curX); |
| + |
| + // Do we need to populate the rest of the block? |
| + if ((finalX - (curX & ~3)) >= kR11_EACBlockSz) { |
| + const int col = curX & 3; |
| + const int colsLeft = 4 - col; |
| + SkASSERT(curX + colsLeft <= finalX); |
| + |
| + UPDATE_BLOCK; |
| + |
| + // Write this block |
| + *outPtr = COMPRESS_BLOCK; |
| + ++outPtr; |
| + curX += colsLeft; |
| + } |
| + |
| + // If we can advance even further, then memset the block and do |
| + // your thing... |
| + if ((finalX - curX) >= kR11_EACBlockSz) { |
| + SkASSERT((curX & 3) == 0); |
| + |
| + const int col = 0; |
| + const int colsLeft = kR11_EACBlockSz; |
| + |
| + UPDATE_BLOCK; |
| + |
| + // While we can keep advancing, just keep writing the block. |
| + uint64_t lastBlock = COMPRESS_BLOCK; |
| + while((finalX - curX) >= kR11_EACBlockSz) { |
| + *outPtr = lastBlock; |
| + ++outPtr; |
| + curX += kR11_EACBlockSz; |
| + } |
| + } |
| + |
| + // If we haven't advanced within the block then do so. |
| + if (curX < finalX) { |
| + const int col = curX & 3; |
| + const int colsLeft = finalX - curX; |
| + |
| + UPDATE_BLOCK; |
| + |
| + curX += colsLeft; |
| + } |
| + |
| + SkASSERT(curX == finalX); |
| + |
| + // Figure out what the next advancement is... |
| + for (int i = 0; i < kR11_EACBlockSz; ++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 < kR11_EACBlockSz; ++i) { |
| + finalX = SkMin32(nextX[i], finalX); |
| + } |
| + } |
| + |
| + // If we didn't land on a block boundary, output the block... |
| + if ((curX & 3) > 1) { |
| + *outPtr = COMPRESS_BLOCK; |
| + } |
| + |
| + #undef COMPRESS_BLOCK |
| + #undef UPDATE_BLOCK |
| + |
| + fNextRun = 0; |
| +} |
| + |
| } // namespace SkTextureCompressor |