Index: src/utils/SkTextureCompressor_Blitter.h |
diff --git a/src/utils/SkTextureCompressor_Blitter.h b/src/utils/SkTextureCompressor_Blitter.h |
index e44a5deade5a8eb63e0dffd73b9b822e95781b42..b0b5cef36b9ead92031715e2b78c49f8ba225068 100644 |
--- a/src/utils/SkTextureCompressor_Blitter.h |
+++ b/src/utils/SkTextureCompressor_Blitter.h |
@@ -43,7 +43,12 @@ public: |
// 0x7FFE is one minus the largest positive 16-bit int. We use it for |
// debugging to make sure that we're properly setting the nextX distance |
// in flushRuns(). |
- : kLongestRun(0x7FFE), kZeroAlpha(0) |
+#ifdef SK_DEBUG |
+ : fBlitMaskCalled(false), |
+#else |
+ : |
+#endif |
+ kLongestRun(0x7FFE), kZeroAlpha(0) |
, fNextRun(0) |
, fWidth(width) |
, fHeight(height) |
@@ -155,17 +160,72 @@ public: |
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!"); |
+ // Blit a pattern of pixels defined by a rectangle-clipped mask; We make an |
+ // assumption here that if this function gets called, then it will replace all |
+ // of the compressed texture blocks that it touches. Hence, two separate calls |
+ // to blitMask that have clips next to one another will cause artifacts. Most |
+ // of the time, however, this function gets called because constructing the mask |
+ // was faster than constructing the RLE for blitAntiH, and this function will |
+ // only be called once. |
+#ifdef SK_DEBUG |
+ bool fBlitMaskCalled; |
+#endif |
+ virtual void blitMask(const SkMask& mask, const SkIRect& clip) SK_OVERRIDE { |
+ |
+ // Assumptions: |
+ SkASSERT(!fBlitMaskCalled && (fBlitMaskCalled = true)); |
+ SkASSERT(SkMask::kA8_Format == mask.fFormat); |
+ SkASSERT(mask.fBounds.contains(clip)); |
+ |
+ // Start from largest block boundary less than the clip boundaries. |
+ const int startI = BlockDim * (clip.left() / BlockDim); |
+ const int startJ = BlockDim * (clip.top() / BlockDim); |
+ |
+ for (int j = startJ; j < clip.bottom(); j += BlockDim) { |
+ |
+ // Get the destination for this block row |
+ uint8_t* dst = this->getBlock(startI, j); |
+ for (int i = startI; i < clip.right(); i += BlockDim) { |
+ |
+ // At this point, the block should intersect the clip. |
+ SkASSERT(SkIRect::IntersectsNoEmptyCheck( |
+ SkIRect::MakeXYWH(i, j, BlockDim, BlockDim), clip)); |
+ |
+ // Do we need to pad it? |
+ if (i < clip.left() || j < clip.top() || |
+ i + BlockDim > clip.right() || j + BlockDim > clip.bottom()) { |
+ |
+ uint8_t block[BlockDim*BlockDim]; |
+ memset(block, 0, sizeof(block)); |
+ |
+ const int startX = SkMax32(i, clip.left()); |
+ const int startY = SkMax32(j, clip.top()); |
+ |
+ const int endX = SkMin32(i + BlockDim, clip.right()); |
+ const int endY = SkMin32(j + BlockDim, clip.bottom()); |
+ |
+ for (int y = startY; y < endY; ++y) { |
+ const int col = startX - i; |
+ const int row = y - j; |
+ const int valsWide = endX - startX; |
+ SkASSERT(valsWide <= BlockDim); |
+ SkASSERT(0 <= col && col < BlockDim); |
+ SkASSERT(0 <= row && row < BlockDim); |
+ memcpy(block + row*BlockDim + col, |
+ mask.getAddr8(startX, j + row), valsWide); |
+ } |
+ |
+ CompressorType::CompressA8Horizontal(dst, block, BlockDim); |
+ } else { |
+ // Otherwise, just compress it. |
+ uint8_t*const src = mask.getAddr8(i, j); |
+ const uint32_t rb = mask.fRowBytes; |
+ CompressorType::CompressA8Horizontal(dst, src, rb); |
+ } |
+ |
+ dst += EncodedBlockSize; |
+ } |
+ } |
} |
// If the blitter just sets a single value for each pixel, return the |