aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/gmmain.cpp6
-rw-r--r--include/core/SkTileGridPicture.h34
-rw-r--r--src/core/SkTileGrid.cpp46
-rw-r--r--src/core/SkTileGrid.h6
-rw-r--r--src/core/SkTileGridPicture.cpp23
-rw-r--r--tests/TileGridTest.cpp110
-rw-r--r--tools/PictureRenderer.cpp7
-rw-r--r--tools/PictureRenderer.h8
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();