aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils
diff options
context:
space:
mode:
authorGravatar krajcevski <krajcevski@google.com>2014-08-11 13:34:22 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-08-11 13:34:22 -0700
commita10555a354cf294bde217044472d33c3161df249 (patch)
tree297e8b603932858883601228a493f406ee3b20ee /src/utils
parent8b2cb3391d75f0e7b522ab3180da833370f413bd (diff)
Add BlitRect to SkTCompressedAlphaBlitter
R=robertphillips@google.com Author: krajcevski@google.com Review URL: https://codereview.chromium.org/456873003
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/SkTextureCompressor_ASTC.cpp19
-rw-r--r--src/utils/SkTextureCompressor_Blitter.h281
-rw-r--r--src/utils/SkTextureCompressor_LATC.cpp70
-rw-r--r--src/utils/SkTextureCompressor_R11EAC.cpp9
4 files changed, 339 insertions, 40 deletions
diff --git a/src/utils/SkTextureCompressor_ASTC.cpp b/src/utils/SkTextureCompressor_ASTC.cpp
index 029f7f2426..c7c7f14019 100644
--- a/src/utils/SkTextureCompressor_ASTC.cpp
+++ b/src/utils/SkTextureCompressor_ASTC.cpp
@@ -2000,6 +2000,12 @@ static void decompress_astc_block(uint8_t* dst, int dstRowBytes,
}
}
+////////////////////////////////////////////////////////////////////////////////
+//
+// ASTC Comrpession Struct
+//
+////////////////////////////////////////////////////////////////////////////////
+
// This is the type passed as the CompressorType argument of the compressed
// blitter for the ASTC format. The static functions required to be in this
// struct are documented in SkTextureCompressor_Blitter.h
@@ -2013,8 +2019,19 @@ struct CompressorASTC {
compress_a8_astc_block<GetAlpha>(&dst, src, srcRowBytes);
}
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src) {
+#if PEDANTIC_BLIT_RECT
+ static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
+ const uint8_t* mask) {
+ // TODO: krajcevski
+ // This is kind of difficult for ASTC because the weight values are calculated
+ // as an average of the actual weights. The best we can do is decompress the
+ // weights and recalculate them based on the new texel values. This should
+ // be "not too bad" since we know that anytime we hit this function, we're
+ // compressing 12x12 block dimension alpha-only, and we know the layout
+ // of the block
+ SkFAIL("Implement me!");
}
+#endif
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/utils/SkTextureCompressor_Blitter.h b/src/utils/SkTextureCompressor_Blitter.h
index 4470201b86..3bf10491b6 100644
--- a/src/utils/SkTextureCompressor_Blitter.h
+++ b/src/utils/SkTextureCompressor_Blitter.h
@@ -13,6 +13,22 @@
namespace SkTextureCompressor {
+// Ostensibly, SkBlitter::BlitRect is supposed to set a rect of pixels to full
+// alpha. This becomes problematic when using compressed texture blitters, since
+// the rect rarely falls along block boundaries. The proper way to handle this is
+// to update the compressed encoding of a block by resetting the proper parameters
+// (and even recompressing the block) where a rect falls inbetween block boundaries.
+// PEDANTIC_BLIT_RECT attempts to do this by requiring the struct passed to
+// SkTCompressedAlphaBlitter to implement an UpdateBlock function call.
+//
+// However, the way that BlitRect gets used almost exclusively is to bracket inverse
+// fills for paths. In other words, the top few rows and bottom few rows of a path
+// that's getting inverse filled are called using blitRect. The rest are called using
+// the standard blitAntiH. As a result, we can just call blitAntiH with a faux RLE
+// of full alpha values, and then check in our flush() call that we don't run off the
+// edge of the buffer. This is why we do not need this flag to be turned on.
+#define PEDANTIC_BLIT_RECT 1
+
// This class implements a blitter that blits directly into a buffer that will
// be used as an compressed alpha texture. We compute this buffer by
// buffering scan lines and then outputting them all at once. The number of
@@ -29,13 +45,17 @@ namespace SkTextureCompressor {
//
// // The function used to compress an A8 block. The layout of the
// // block is also expected to be in row-major order.
-// static void CompressA8Horizontal(uint8_t* dst, const uint8_t block[]);
+// static void CompressA8Horizontal(uint8_t* dst, const uint8_t* src, int srcRowBytes);
//
+#if PEDANTIC_BLIT_RECT
// // The function used to update an already compressed block. This will
-// // most likely be implementation dependent.
-// static void UpdateBlock(uint8_t* dst, const uint8_t* src);
+// // most likely be implementation dependent. The mask variable will have
+// // 0xFF in positions where the block should be updated and 0 in positions
+// // where it shouldn't. src contains an uncompressed buffer of pixels.
+// static void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
+// const uint8_t* mask);
+#endif
// };
-//
template<int BlockDim, int EncodedBlockSize, typename CompressorType>
class SkTCompressedAlphaBlitter : public SkBlitter {
public:
@@ -44,7 +64,8 @@ public:
// debugging to make sure that we're properly setting the nextX distance
// in flushRuns().
#ifdef SK_DEBUG
- : fBlitMaskCalled(false),
+ : fCalledOnceWithNonzeroY(false)
+ , fBlitMaskCalled(false),
#else
:
#endif
@@ -73,6 +94,8 @@ public:
virtual void blitAntiH(int x, int y,
const SkAlpha antialias[],
const int16_t runs[]) SK_OVERRIDE {
+ SkASSERT(0 == x);
+
// Make sure that the new row to blit is either the first
// row that we're blitting, or it's exactly the next scan row
// since the last row that we blit. This is to ensure that when
@@ -131,12 +154,122 @@ public:
SkFAIL("Not implemented!");
}
- // Blit a solid rectangle one or more pixels wide.
+ // Blit a solid rectangle one or more pixels wide. It's assumed that blitRect
+ // is called as a way to bracket blitAntiH where above and below the path the
+ // called path just needs a solid rectangle to fill in the mask.
+#ifdef SK_DEBUG
+ bool fCalledOnceWithNonzeroY;
+#endif
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!");
+
+ // Assumptions:
+ SkASSERT(0 == x);
+ SkASSERT(width <= fWidth);
+
+ // Make sure that we're only ever bracketing calls to blitAntiH.
+ SkASSERT((0 == y) || (!fCalledOnceWithNonzeroY && (fCalledOnceWithNonzeroY = true)));
+
+#if !(PEDANTIC_BLIT_RECT)
+ for (int i = 0; i < height; ++i) {
+ const SkAlpha kFullAlpha = 0xFF;
+ this->blitAntiH(x, y+i, &kFullAlpha, &kLongestRun);
+ }
+#else
+ const int startBlockX = (x / BlockDim) * BlockDim;
+ const int startBlockY = (y / BlockDim) * BlockDim;
+
+ const int endBlockX = ((x + width) / BlockDim) * BlockDim;
+ const int endBlockY = ((y + height) / BlockDim) * BlockDim;
+
+ // If start and end are the same, then we only need to update a single block...
+ if (startBlockY == endBlockY && startBlockX == endBlockX) {
+ uint8_t mask[BlockDim*BlockDim];
+ memset(mask, 0, sizeof(mask));
+
+ const int xoff = x - startBlockX;
+ SkASSERT((xoff + width) <= BlockDim);
+
+ const int yoff = y - startBlockY;
+ SkASSERT((yoff + height) <= BlockDim);
+
+ for (int j = 0; j < height; ++j) {
+ memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, width);
+ }
+
+ uint8_t* dst = this->getBlock(startBlockX, startBlockY);
+ CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
+
+ // If start and end are the same in the y dimension, then we can freely update an
+ // entire row of blocks...
+ } else if (startBlockY == endBlockY) {
+
+ this->updateBlockRow(x, y, width, height, startBlockY, startBlockX, endBlockX);
+
+ // Similarly, if the start and end are in the same column, then we can just update
+ // an entire column of blocks...
+ } else if (startBlockX == endBlockX) {
+
+ this->updateBlockCol(x, y, width, height, startBlockX, startBlockY, endBlockY);
+
+ // Otherwise, the rect spans a non-trivial region of blocks, and we have to construct
+ // a kind of 9-patch to update each of the pieces of the rect. The top and bottom
+ // rows are updated using updateBlockRow, and the left and right columns are updated
+ // using updateBlockColumn. Anything in the middle is simply memset to an opaque block
+ // encoding.
+ } else {
+
+ const int innerStartBlockX = startBlockX + BlockDim;
+ const int innerStartBlockY = startBlockY + BlockDim;
+
+ // Blit top row
+ const int topRowHeight = innerStartBlockY - y;
+ this->updateBlockRow(x, y, width, topRowHeight, startBlockY,
+ startBlockX, endBlockX);
+
+ // Advance y
+ y += topRowHeight;
+ height -= topRowHeight;
+
+ // Blit middle
+ if (endBlockY > innerStartBlockY) {
+
+ // Update left row
+ this->updateBlockCol(x, y, innerStartBlockX - x, endBlockY, startBlockY,
+ startBlockX, innerStartBlockX);
+
+ // Update the middle with an opaque encoding...
+ uint8_t mask[BlockDim*BlockDim];
+ memset(mask, 0xFF, sizeof(mask));
+
+ uint8_t opaqueEncoding[EncodedBlockSize];
+ CompressorType::CompressA8Horizontal(opaqueEncoding, mask, BlockDim);
+
+ for (int j = innerStartBlockY; j < endBlockY; j += BlockDim) {
+ uint8_t* opaqueDst = this->getBlock(innerStartBlockX, j);
+ for (int i = innerStartBlockX; i < endBlockX; i += BlockDim) {
+ memcpy(opaqueDst, opaqueEncoding, EncodedBlockSize);
+ opaqueDst += EncodedBlockSize;
+ }
+ }
+
+ // If we need to update the right column, do that too
+ if (x + width > endBlockX) {
+ this->updateBlockCol(endBlockX, y, x + width - endBlockX, endBlockY,
+ endBlockX, innerStartBlockY, endBlockY);
+ }
+
+ // Advance y
+ height = y + height - endBlockY;
+ y = endBlockY;
+ }
+
+ // If we need to update the last row, then do that, too.
+ if (height > 0) {
+ this->updateBlockRow(x, y, width, height, endBlockY,
+ startBlockX, endBlockX);
+ }
+ }
+#endif
}
// Blit a rectangle with one alpha-blended column on the left,
@@ -400,6 +533,12 @@ private:
// Make sure that we have a valid right-bound X value
SkASSERT(finalX < 0xFFFFF);
+ // If the finalX is the longest run, then just blit until we have
+ // width...
+ if (kLongestRun == finalX) {
+ finalX = fWidth;
+ }
+
// Run the blitter...
while (curX != finalX) {
SkASSERT(finalX >= curX);
@@ -449,19 +588,23 @@ private:
SkASSERT(curX == finalX);
// Figure out what the next advancement is...
- for (int i = 0; i < BlockDim; ++i) {
- if (nextX[i] == finalX) {
- const int16_t run = *(fBufferedRuns[i].fRuns);
- fBufferedRuns[i].fRuns += run;
- fBufferedRuns[i].fAlphas += run;
- curAlpha[i] = *(fBufferedRuns[i].fAlphas);
- nextX[i] += *(fBufferedRuns[i].fRuns);
+ if (finalX < fWidth) {
+ for (int i = 0; i < BlockDim; ++i) {
+ if (nextX[i] == finalX) {
+ const int16_t run = *(fBufferedRuns[i].fRuns);
+ fBufferedRuns[i].fRuns += run;
+ fBufferedRuns[i].fAlphas += run;
+ curAlpha[i] = *(fBufferedRuns[i].fAlphas);
+ nextX[i] += *(fBufferedRuns[i].fRuns);
+ }
}
- }
- finalX = 0xFFFFF;
- for (int i = 0; i < BlockDim; ++i) {
- finalX = SkMin32(nextX[i], finalX);
+ finalX = 0xFFFFF;
+ for (int i = 0; i < BlockDim; ++i) {
+ finalX = SkMin32(nextX[i], finalX);
+ }
+ } else {
+ curX = finalX;
}
}
@@ -483,6 +626,102 @@ private:
fNextRun = 0;
}
+
+#if PEDANTIC_BLIT_RECT
+ void updateBlockRow(int x, int y, int width, int height,
+ int blockRow, int startBlockX, int endBlockX) {
+ if (0 == width || 0 == height || startBlockX == endBlockX) {
+ return;
+ }
+
+ uint8_t* dst = this->getBlock(startBlockX, BlockDim * (y / BlockDim));
+
+ // One horizontal strip to update
+ uint8_t mask[BlockDim*BlockDim];
+ memset(mask, 0, sizeof(mask));
+
+ // Update the left cap
+ int blockX = startBlockX;
+ const int yoff = y - blockRow;
+ for (int j = 0; j < height; ++j) {
+ const int xoff = x - blockX;
+ memset(mask + (j + yoff)*BlockDim + xoff, 0xFF, BlockDim - xoff);
+ }
+ CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
+ dst += EncodedBlockSize;
+ blockX += BlockDim;
+
+ // Update the middle
+ if (blockX < endBlockX) {
+ for (int j = 0; j < height; ++j) {
+ memset(mask + (j + yoff)*BlockDim, 0xFF, BlockDim);
+ }
+ while (blockX < endBlockX) {
+ CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
+ dst += EncodedBlockSize;
+ blockX += BlockDim;
+ }
+ }
+
+ SkASSERT(endBlockX == blockX);
+
+ // Update the right cap (if we need to)
+ if (x + width > endBlockX) {
+ memset(mask, 0, sizeof(mask));
+ for (int j = 0; j < height; ++j) {
+ const int xoff = (x+width-blockX);
+ memset(mask + (j+yoff)*BlockDim, 0xFF, xoff);
+ }
+ CompressorType::UpdateBlock(dst, mask, BlockDim, mask);
+ }
+ }
+
+ void updateBlockCol(int x, int y, int width, int height,
+ int blockCol, int startBlockY, int endBlockY) {
+ if (0 == width || 0 == height || startBlockY == endBlockY) {
+ return;
+ }
+
+ // One vertical strip to update
+ uint8_t mask[BlockDim*BlockDim];
+ memset(mask, 0, sizeof(mask));
+ const int maskX0 = x - blockCol;
+ const int maskWidth = maskX0 + width;
+ SkASSERT(maskWidth <= BlockDim);
+
+ // Update the top cap
+ int blockY = startBlockY;
+ for (int j = (y - blockY); j < BlockDim; ++j) {
+ memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth);
+ }
+ CompressorType::UpdateBlock(this->getBlock(blockCol, blockY), mask, BlockDim, mask);
+ blockY += BlockDim;
+
+ // Update middle
+ if (blockY < endBlockY) {
+ for (int j = 0; j < BlockDim; ++j) {
+ memset(mask + maskX0 + j*BlockDim, 0xFF, maskWidth);
+ }
+ while (blockY < endBlockY) {
+ CompressorType::UpdateBlock(this->getBlock(blockCol, blockY),
+ mask, BlockDim, mask);
+ blockY += BlockDim;
+ }
+ }
+
+ SkASSERT(endBlockY == blockY);
+
+ // Update bottom
+ if (y + height > endBlockY) {
+ for (int j = y+height; j < endBlockY + BlockDim; ++j) {
+ memset(mask + (j-endBlockY)*BlockDim, 0, BlockDim);
+ }
+ CompressorType::UpdateBlock(this->getBlock(blockCol, blockY),
+ mask, BlockDim, mask);
+ }
+ }
+#endif // PEDANTIC_BLIT_RECT
+
};
} // namespace SkTextureCompressor
diff --git a/src/utils/SkTextureCompressor_LATC.cpp b/src/utils/SkTextureCompressor_LATC.cpp
index c9bf89ea4c..1e5e142ddb 100644
--- a/src/utils/SkTextureCompressor_LATC.cpp
+++ b/src/utils/SkTextureCompressor_LATC.cpp
@@ -297,8 +297,27 @@ static uint64_t compress_latc_block(const uint8_t pixels[]) {
#if COMPRESS_LATC_FAST
-// Take the top three indices of each int and pack them into the low 12
+// Take the top three bits of each index and pack them into the low 12
// bits of the integer.
+static inline uint32_t pack_index(uint32_t x) {
+ // Pack it in...
+#if defined (SK_CPU_BENDIAN)
+ return
+ (x >> 24) |
+ ((x >> 13) & 0x38) |
+ ((x >> 2) & 0x1C0) |
+ ((x << 9) & 0xE00);
+#else
+ return
+ (x & 0x7) |
+ ((x >> 5) & 0x38) |
+ ((x >> 10) & 0x1C0) |
+ ((x >> 15) & 0xE00);
+#endif
+}
+
+// Converts each 8-bit byte in the integer into an LATC index, and then packs
+// the indices into the low 12 bits of the integer.
static inline uint32_t convert_index(uint32_t x) {
// Since the palette is
// 255, 0, 219, 182, 146, 109, 73, 36
@@ -326,21 +345,8 @@ static inline uint32_t convert_index(uint32_t x) {
// Mask out high bits:
// 9 7 6 5 4 3 2 0 --> 1 7 6 5 4 3 2 0
x &= 0x07070707;
-
- // Pack it in...
-#if defined (SK_CPU_BENDIAN)
- return
- (x >> 24) |
- ((x >> 13) & 0x38) |
- ((x >> 2) & 0x1C0) |
- ((x << 9) & 0xE00);
-#else
- return
- (x & 0x7) |
- ((x >> 5) & 0x38) |
- ((x >> 10) & 0x1C0) |
- ((x >> 15) & 0xE00);
-#endif
+
+ return pack_index(x);
}
typedef uint64_t (*PackIndicesProc)(const uint8_t* alpha, int rowBytes);
@@ -427,8 +433,38 @@ struct CompressorLATC {
compress_a8_latc_block<PackRowMajor>(&dst, src, srcRowBytes);
}
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src) {
+#if PEDANTIC_BLIT_RECT
+ static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
+ const uint8_t* mask) {
+ // Pack the mask
+ uint64_t cmpMask = 0;
+ for (int i = 0; i < 4; ++i) {
+ const uint32_t idx = *(reinterpret_cast<const uint32_t*>(src + i*srcRowBytes));
+ cmpMask |= static_cast<uint64_t>(pack_index(idx)) << 12*i;
+ }
+ cmpMask = SkEndian_SwapLE64(cmpMask << 16); // avoid header
+
+ uint64_t cmpSrc;
+ uint8_t *cmpSrcPtr = reinterpret_cast<uint8_t*>(&cmpSrc);
+ compress_a8_latc_block<PackRowMajor>(&cmpSrcPtr, src, srcRowBytes);
+
+ // Mask out header
+ cmpSrc = cmpSrc & cmpMask;
+
+ // Read destination encoding
+ uint64_t *cmpDst = reinterpret_cast<uint64_t*>(dst);
+
+ // If the destination is the encoding for a blank block, then we need
+ // to properly set the header
+ if (0 == cmpDst) {
+ *cmpDst = SkTEndian_SwapLE64(0x24924924924900FFULL);
+ }
+
+ // Set the new indices
+ *cmpDst &= ~cmpMask;
+ *cmpDst |= cmpSrc;
}
+#endif // PEDANTIC_BLIT_RECT
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/utils/SkTextureCompressor_R11EAC.cpp b/src/utils/SkTextureCompressor_R11EAC.cpp
index 5ccd50bd32..9996eb9596 100644
--- a/src/utils/SkTextureCompressor_R11EAC.cpp
+++ b/src/utils/SkTextureCompressor_R11EAC.cpp
@@ -603,8 +603,15 @@ struct CompressorR11EAC {
*(reinterpret_cast<uint64_t*>(dst)) = compress_r11eac_block_fast(src, srcRowBytes);
}
- static inline void UpdateBlock(uint8_t* dst, const uint8_t* src) {
+#if PEDANTIC_BLIT_RECT
+ static inline void UpdateBlock(uint8_t* dst, const uint8_t* src, int srcRowBytes,
+ const uint8_t* mask) {
+ // TODO: krajcevski
+ // The implementation of this function should be similar to that of LATC, since
+ // the R11EAC indices directly correspond to pixel values.
+ SkFAIL("Implement me!");
}
+#endif
};
////////////////////////////////////////////////////////////////////////////////