diff options
-rw-r--r-- | gm/gmmain.cpp | 6 | ||||
-rw-r--r-- | include/core/SkTileGridPicture.h | 34 | ||||
-rw-r--r-- | src/core/SkTileGrid.cpp | 46 | ||||
-rw-r--r-- | src/core/SkTileGrid.h | 6 | ||||
-rw-r--r-- | src/core/SkTileGridPicture.cpp | 23 | ||||
-rw-r--r-- | tests/TileGridTest.cpp | 110 | ||||
-rw-r--r-- | tools/PictureRenderer.cpp | 7 | ||||
-rw-r--r-- | tools/PictureRenderer.h | 8 |
8 files changed, 183 insertions, 57 deletions
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 611b58188f..16fb7c7337 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -806,7 +806,11 @@ public: int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale)); if (kTileGrid_BbhType == bbhType) { - pict = new SkTileGridPicture(16, 16, width, height); + SkTileGridPicture::TileGridInfo info; + info.fMargin.setEmpty(); + info.fOffset.setZero(); + info.fTileInterval.set(16, 16); + pict = new SkTileGridPicture(width, height, info); } else { pict = new SkPicture; } diff --git a/include/core/SkTileGridPicture.h b/include/core/SkTileGridPicture.h index 28d545a467..b28f3d3b55 100644 --- a/include/core/SkTileGridPicture.h +++ b/include/core/SkTileGridPicture.h @@ -9,6 +9,8 @@ #define SkTileGridPicture_DEFINED #include "SkPicture.h" +#include "SkPoint.h" +#include "SkSize.h" /** * Subclass of SkPicture that override the behavior of the @@ -20,23 +22,35 @@ */ class SK_API SkTileGridPicture : public SkPicture { public: + struct TileGridInfo { + /** Tile placement interval */ + SkISize fTileInterval; + + /** Pixel coverage overlap between adjacent tiles */ + SkISize fMargin; + + /** Offset added to device-space bounding box positions to convert + * them to tile-grid space. This can be used to adjust the "phase" + * of the tile grid to match probable query rectangles that will be + * used to search into the tile grid. As long as the offset is smaller + * or equal to the margin, there is no need to extend the domain of + * the tile grid to prevent data loss. + */ + SkIPoint fOffset; + }; /** * Constructor - * @param tileWidth horizontal stride between consecutive tiles - * @param tileHeight vertical stride between consecutive tiles * @param width recording canvas width in device pixels * @param height recording canvas height in device pixels - * @param borderPixels pixels of overlap between adjacent tiles. Set this - * value to match the border overlap that is applied to tiles by user - * code. Properly setting this value will help improve performance - * when performing tile-aligned playbacks with query regions that - * match tile bounds outset by borderPixels pixels. Such outsets - * are often used to prevent filtering artifacts at tile boundaries. + * @param info description of the tiling layout */ - SkTileGridPicture(int tileWidth, int tileHeight, int width, int height, int borderPixels = 0); + SkTileGridPicture(int width, int height, const TileGridInfo& info); + virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE; + private: - int fTileWidth, fTileHeight, fXTileCount, fYTileCount, fBorderPixels; + int fXTileCount, fYTileCount; + TileGridInfo fInfo; }; #endif diff --git a/src/core/SkTileGrid.cpp b/src/core/SkTileGrid.cpp index 7e078517ea..8a299496ac 100644 --- a/src/core/SkTileGrid.cpp +++ b/src/core/SkTileGrid.cpp @@ -8,19 +8,20 @@ #include "SkTileGrid.h" -SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, - int borderPixels, SkTileGridNextDatumFunctionPtr nextDatumFunction) +SkTileGrid::SkTileGrid(int xTileCount, int yTileCount, const SkTileGridPicture::TileGridInfo& info, + SkTileGridNextDatumFunctionPtr nextDatumFunction) { - fTileWidth = tileWidth; - fTileHeight = tileHeight; fXTileCount = xTileCount; fYTileCount = yTileCount; - // Border padding is offset by 1 as a provision for AA and + fInfo = info; + // Margin is offset by 1 as a provision for AA and // to cancel-out the outset applied by getClipDeviceBounds. - fBorderPixels = borderPixels + 1; + fInfo.fMargin.fHeight++; + fInfo.fMargin.fWidth++; fTileCount = fXTileCount * fYTileCount; fInsertionCount = 0; - fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount); + fGridBounds = SkIRect::MakeXYWH(0, 0, fInfo.fTileInterval.width() * fXTileCount, + fInfo.fTileInterval.height() * fYTileCount); fNextDatumFunction = nextDatumFunction; fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount); } @@ -36,16 +37,22 @@ SkTDArray<void *>& SkTileGrid::tile(int x, int y) { void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) { SkASSERT(!bounds.isEmpty()); SkIRect dilatedBounds = bounds; - dilatedBounds.outset(fBorderPixels, fBorderPixels); - + dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height()); + dilatedBounds.offset(fInfo.fOffset); if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) { return; } - int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fTileWidth, fXTileCount - 1), 0); - int maxTileX = SkMax32(SkMin32(dilatedBounds.right() / fTileWidth, fXTileCount - 1), 0); - int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fTileHeight, fYTileCount -1), 0); - int maxTileY = SkMax32(SkMin32(dilatedBounds.bottom() / fTileHeight, fYTileCount -1), 0); + // Note: SkIRects are non-inclusive of the right() column and bottom() row, + // hence the "-1"s in the computations of maxTileX and maxTileY. + int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), + fXTileCount - 1), 0); + int maxTileX = SkMax32(SkMin32((dilatedBounds.right() - 1) / fInfo.fTileInterval.width(), + fXTileCount - 1), 0); + int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), + fYTileCount -1), 0); + int maxTileY = SkMax32(SkMin32((dilatedBounds.bottom() -1) / fInfo.fTileInterval.height(), + fYTileCount -1), 0); for (int x = minTileX; x <= maxTileX; x++) { for (int y = minTileY; y <= maxTileY; y++) { @@ -56,13 +63,18 @@ void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) { } void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) { + SkIRect adjustedQuery = query; + adjustedQuery.inset(fInfo.fMargin.width(), fInfo.fMargin.height()); + adjustedQuery.offset(fInfo.fOffset); // Convert the query rectangle from device coordinates to tile coordinates // by rounding outwards to the nearest tile boundary so that the resulting tile // region includes the query rectangle. (using truncating division to "floor") - int tileStartX = (query.left() + fBorderPixels) / fTileWidth; - int tileEndX = (query.right() + fTileWidth - fBorderPixels) / fTileWidth; - int tileStartY = (query.top() + fBorderPixels) / fTileHeight; - int tileEndY = (query.bottom() + fTileHeight - fBorderPixels) / fTileHeight; + int tileStartX = adjustedQuery.left() / fInfo.fTileInterval.width(); + int tileEndX = (adjustedQuery.right() + fInfo.fTileInterval.width() - 1) / + fInfo.fTileInterval.width(); + int tileStartY = adjustedQuery.top() / fInfo.fTileInterval.height(); + int tileEndY = (adjustedQuery.bottom() + fInfo.fTileInterval.height() - 1) / + fInfo.fTileInterval.height(); if (tileStartX >= fXTileCount || tileStartY >= fYTileCount || tileEndX <= 0 || tileEndY <= 0) { return; // query does not intersect the grid } diff --git a/src/core/SkTileGrid.h b/src/core/SkTileGrid.h index c0fd3ddaef..c83a0fd468 100644 --- a/src/core/SkTileGrid.h +++ b/src/core/SkTileGrid.h @@ -11,6 +11,7 @@ #include "SkBBoxHierarchy.h" #include "SkPictureStateTree.h" +#include "SkTileGridPicture.h" // for TileGridInfo /** * Subclass of SkBBoxHierarchy that stores elements in buckets that correspond @@ -26,7 +27,7 @@ class SkTileGrid : public SkBBoxHierarchy { public: typedef void* (*SkTileGridNextDatumFunctionPtr)(SkTDArray<void*>** tileData, SkTDArray<int>& tileIndices); - SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, int borderPixels, + SkTileGrid(int xTileCount, int yTileCount, const SkTileGridPicture::TileGridInfo& info, SkTileGridNextDatumFunctionPtr nextDatumFunction); virtual ~SkTileGrid(); @@ -61,7 +62,8 @@ public: private: SkTDArray<void*>& tile(int x, int y); - int fTileWidth, fTileHeight, fXTileCount, fYTileCount, fTileCount, fBorderPixels; + int fXTileCount, fYTileCount, fTileCount; + SkTileGridPicture::TileGridInfo fInfo; SkTDArray<void*>* fTileData; int fInsertionCount; SkIRect fGridBounds; diff --git a/src/core/SkTileGridPicture.cpp b/src/core/SkTileGridPicture.cpp index 8a39d4949b..7a8d5932ac 100644 --- a/src/core/SkTileGridPicture.cpp +++ b/src/core/SkTileGridPicture.cpp @@ -10,18 +10,19 @@ #include "SkPictureStateTree.h" #include "SkTileGrid.h" - -SkTileGridPicture::SkTileGridPicture(int tileWidth, int tileHeight, int width, int height, - int borderPixels) { - SkASSERT(borderPixels >= 0); - fTileWidth = tileWidth; - fTileHeight = tileHeight; - fXTileCount = (width + tileWidth - 1) / tileWidth; - fYTileCount = (height + tileHeight - 1) / tileHeight; - fBorderPixels = borderPixels; +SkTileGridPicture::SkTileGridPicture(int width, int height, const TileGridInfo& info) { + SkASSERT(info.fMargin.width() >= 0); + SkASSERT(info.fMargin.height() >= 0); + fInfo = info; + // Note: SkIRects are non-inclusive of the right() column and bottom() row. + // For example, an SkIRect at 0,0 with a size of (1,1) will only have + // content at pixel (0,0) and will report left=0 and right=1, hence the + // "-1"s below. + fXTileCount = (width + info.fTileInterval.width() - 1) / info.fTileInterval.width(); + fYTileCount = (height + info.fTileInterval.height() - 1) / info.fTileInterval.height(); } SkBBoxHierarchy* SkTileGridPicture::createBBoxHierarchy() const { - return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount, - fBorderPixels, SkTileGridNextDatum<SkPictureStateTree::Draw>)); + return SkNEW_ARGS(SkTileGrid, (fXTileCount, fYTileCount, fInfo, + SkTileGridNextDatum<SkPictureStateTree::Draw>)); } diff --git a/tests/TileGridTest.cpp b/tests/TileGridTest.cpp index a72fd69e4c..8ff629c07e 100644 --- a/tests/TileGridTest.cpp +++ b/tests/TileGridTest.cpp @@ -41,7 +41,11 @@ class TileGridTest { public: static void verifyTileHits(skiatest::Reporter* reporter, SkIRect rect, uint32_t tileMask, int borderPixels = 0) { - SkTileGrid grid(10, 10, 2, 2, borderPixels, NULL); + SkTileGridPicture::TileGridInfo info; + info.fMargin.set(borderPixels, borderPixels); + info.fOffset.setZero(); + info.fTileInterval.set(10 - 2 * borderPixels, 10 - 2 * borderPixels); + SkTileGrid grid(2, 2, info, NULL); grid.insert(NULL, rect, false); REPORTER_ASSERT(reporter, grid.tile(0,0).count() == ((tileMask & kTopLeft_Tile)? 1 : 0)); @@ -55,7 +59,11 @@ public: static void TestUnalignedQuery(skiatest::Reporter* reporter) { // Use SkTileGridPicture to generate a SkTileGrid with a helper - SkTileGridPicture picture(10, 10, 20, 20); + SkTileGridPicture::TileGridInfo info; + info.fMargin.setEmpty(); + info.fOffset.setZero(); + info.fTileInterval.set(10, 10); + SkTileGridPicture picture(20, 20, info); SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(8), SkIntToScalar(8)); SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11), @@ -107,6 +115,86 @@ public: } } + static void TestOverlapOffsetQueryAlignment(skiatest::Reporter* reporter) { + // Use SkTileGridPicture to generate a SkTileGrid with a helper + SkTileGridPicture::TileGridInfo info; + info.fMargin.set(1, 1); + info.fOffset.set(-1, -1); + info.fTileInterval.set(8, 8); + SkTileGridPicture picture(20, 20, info); + + // rect landing entirely in top left tile + SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(1), SkIntToScalar(1)); + // rect landing entirely in center tile + SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(12), SkIntToScalar(12), + SkIntToScalar(1), SkIntToScalar(1)); + // rect landing entirely in bottomright tile + SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(19), SkIntToScalar(19), + SkIntToScalar(1), SkIntToScalar(1)); + SkCanvas* canvas = picture.beginRecording(20, 20, SkPicture::kOptimizeForClippedPlayback_RecordingFlag); + SkPaint paint; + canvas->drawRect(rect1, paint); + canvas->drawRect(rect2, paint); + canvas->drawRect(rect3, paint); + picture.endRecording(); + + SkBitmap tileBitmap; + tileBitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + tileBitmap.allocPixels(); + SkBitmap moreThanATileBitmap; + moreThanATileBitmap.setConfig(SkBitmap::kARGB_8888_Config, 11, 11); + moreThanATileBitmap.allocPixels(); + // Test parts of top-left tile + { + // The offset should cancel the top and left borders of the top left tile + // So a look-up at interval 0-10 should be grid aligned, + SkDevice device(tileBitmap); + MockCanvas mockCanvas(&device); + picture.draw(&mockCanvas); + REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); + REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); + } + { + // Encroaching border by one pixel + SkDevice device(moreThanATileBitmap); + MockCanvas mockCanvas(&device); + picture.draw(&mockCanvas); + REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); + REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); + REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]); + } + { + // Tile stride is 8 (tileWidth - 2 * border pixels + // so translating by 8, should make query grid-aligned + // with middle tile. + SkDevice device(tileBitmap); + MockCanvas mockCanvas(&device); + mockCanvas.translate(SkIntToScalar(-8), SkIntToScalar(-8)); + picture.draw(&mockCanvas); + REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count()); + REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); + } + { + SkDevice device(tileBitmap); + MockCanvas mockCanvas(&device); + mockCanvas.translate(SkFloatToScalar(-7.9f), SkFloatToScalar(-7.9f)); + picture.draw(&mockCanvas); + REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); + REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]); + REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]); + } + { + SkDevice device(tileBitmap); + MockCanvas mockCanvas(&device); + mockCanvas.translate(SkFloatToScalar(-8.1f), SkFloatToScalar(-8.1f)); + picture.draw(&mockCanvas); + REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count()); + REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]); + REPORTER_ASSERT(reporter, rect3 == mockCanvas.fRects[1]); + } + } + static void Test(skiatest::Reporter* reporter) { // Out of bounds verifyTileHits(reporter, SkIRect::MakeXYWH(30, 0, 1, 1), 0); @@ -115,16 +203,19 @@ public: verifyTileHits(reporter, SkIRect::MakeXYWH(0, -10, 1, 1), 0); // Dilation for AA consideration - verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 8, 8), kTopLeft_Tile); - verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 9, 9), kAll_Tile); + verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 9, 9), kTopLeft_Tile); + verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 10, 10), kAll_Tile); + verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1), kAll_Tile); verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1), kAll_Tile); verifyTileHits(reporter, SkIRect::MakeXYWH(11, 11, 1, 1), kBottomRight_Tile); - + // BorderPixels - verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 7, 7), kTopLeft_Tile, 1); - verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 8, 8), kAll_Tile, 1); - verifyTileHits(reporter, SkIRect::MakeXYWH(11, 11, 1, 1), kAll_Tile, 1); - verifyTileHits(reporter, SkIRect::MakeXYWH(12, 12, 1, 1), kBottomRight_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 6, 6), kTopLeft_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 7, 7), kAll_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1), kAll_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1), kBottomRight_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(17, 17, 1, 1), kBottomRight_Tile, 1); + verifyTileHits(reporter, SkIRect::MakeXYWH(18, 18, 1, 1), 0, 1); // BBoxes that overlap tiles verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 1), kTopLeft_Tile | kTopRight_Tile); @@ -134,6 +225,7 @@ public: verifyTileHits(reporter, SkIRect::MakeXYWH(-10, -10, 40, 40), kAll_Tile); TestUnalignedQuery(reporter); + TestOverlapOffsetQueryAlignment(reporter); } }; diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp index 16768c79e2..caab64a98d 100644 --- a/tools/PictureRenderer.cpp +++ b/tools/PictureRenderer.cpp @@ -42,6 +42,9 @@ enum { void PictureRenderer::init(SkPicture* pict) { SkASSERT(NULL == fPicture); SkASSERT(NULL == fCanvas.get()); + fGridInfo.fMargin.setEmpty(); + fGridInfo.fOffset.setZero(); + fGridInfo.fTileInterval.set(1, 1); if (fPicture != NULL || NULL != fCanvas.get()) { return; } @@ -800,8 +803,8 @@ SkPicture* PictureRenderer::createPicture() { case kRTree_BBoxHierarchyType: return SkNEW(RTreePicture); case kTileGrid_BBoxHierarchyType: - return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(), - fPicture->height())); + return SkNEW_ARGS(SkTileGridPicture, (fPicture->width(), + fPicture->height(), fGridInfo)); } SkASSERT(0); // invalid bbhType return NULL; diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h index b054a546fe..47fc7d7a31 100644 --- a/tools/PictureRenderer.h +++ b/tools/PictureRenderer.h @@ -19,6 +19,7 @@ #include "SkString.h" #include "SkTDArray.h" #include "SkThreadPool.h" +#include "SkTileGridPicture.h" #include "SkTypes.h" #if SK_SUPPORT_GPU @@ -170,8 +171,7 @@ public: } void setGridSize(int width, int height) { - fGridWidth = width; - fGridHeight = height; + fGridInfo.fTileInterval.set(width, height); } bool isUsingBitmapDevice() { @@ -255,8 +255,6 @@ public: : fPicture(NULL) , fDeviceType(kBitmap_DeviceType) , fBBoxHierarchyType(kNone_BBoxHierarchyType) - , fGridWidth(0) - , fGridHeight(0) , fScaleFactor(SK_Scalar1) #if SK_SUPPORT_GPU , fGrContext(NULL) @@ -279,7 +277,7 @@ protected: BBoxHierarchyType fBBoxHierarchyType; DrawFilterFlags fDrawFilters[SkDrawFilter::kTypeCount]; SkString fDrawFiltersConfig; - int fGridWidth, fGridHeight; // used when fBBoxHierarchyType is TileGrid + SkTileGridPicture::TileGridInfo fGridInfo; // used when fBBoxHierarchyType is TileGrid void buildBBoxHierarchy(); |