Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(568)

Unified Diff: src/utils/SkTextureCompressor_Blitter.h

Issue 456873003: Add BlitRect to SkTCompressedAlphaBlitter (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Small nit Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/utils/SkTextureCompressor_ASTC.cpp ('k') | src/utils/SkTextureCompressor_LATC.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/utils/SkTextureCompressor_Blitter.h
diff --git a/src/utils/SkTextureCompressor_Blitter.h b/src/utils/SkTextureCompressor_Blitter.h
index 4470201b861d71c1de609c9546543ab36f2fbb03..3bf10491b6dc608d18ceae3ef0306d266562f5c6 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
« no previous file with comments | « src/utils/SkTextureCompressor_ASTC.cpp ('k') | src/utils/SkTextureCompressor_LATC.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698