Modifying SkTileGrid to support arbitrary query rectangles.

Exposing SkTileGrid functionality in the public API through SkTileGridPicture.
This patch also makes TileGrid and Rtree testable in gm, which revealed errors.

TEST=gm with '--tileGrid'
BUG=http://code.google.com/p/chromium/issues/detail?id=164636
Review URL: https://codereview.appspot.com/6933044

git-svn-id: http://skia.googlecode.com/svn/trunk@6783 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
junov@chromium.org 2012-12-13 16:39:53 +00:00
parent 4c1f091b68
commit 3cb834bd27
9 changed files with 262 additions and 50 deletions

View File

@ -39,6 +39,8 @@ namespace skiagm {
kSkipPipe_Flag = 1 << 2,
kSkipTiled_Flag = 1 << 3,
kSkip565_Flag = 1 << 4,
kRTree_Flag = 1 << 5,
kTileGrid_Flag = 1 << 6,
};
void draw(SkCanvas*);

View File

@ -29,6 +29,7 @@
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTArray.h"
#include "SkTileGridPicture.h"
#include "SamplePipeControllers.h"
#if SK_SUPPORT_GPU
@ -129,10 +130,16 @@ private:
};
enum Backend {
kRaster_Backend,
kGPU_Backend,
kPDF_Backend,
kXPS_Backend,
kRaster_Backend,
kGPU_Backend,
kPDF_Backend,
kXPS_Backend,
};
enum BbhType {
kNone_BbhType,
kRTree_BbhType,
kTileGrid_BbhType,
};
enum ConfigFlags {
@ -176,7 +183,6 @@ static PipeFlagComboData gPipeWritingFlagCombos[] = {
| SkGPipeWriter::kSharedAddressSpace_Flag }
};
class GMMain {
public:
GMMain() {
@ -618,11 +624,18 @@ public:
return retval;
}
static SkPicture* generate_new_picture(GM* gm) {
static SkPicture* generate_new_picture(GM* gm, BbhType bbhType) {
// Pictures are refcounted so must be on heap
SkPicture* pict = new SkPicture;
SkPicture* pict;
SkISize size = gm->getISize();
SkCanvas* cv = pict->beginRecording(size.width(), size.height());
if (kTileGrid_BbhType == bbhType) {
pict = new SkTileGridPicture(16, 16, size.width(), size.height());
} else {
pict = new SkPicture;
}
uint32_t recordFlags = (kNone_BbhType == bbhType) ?
0 : SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
SkCanvas* cv = pict->beginRecording(size.width(), size.height(), recordFlags);
invokeGM(gm, cv, false, false);
pict->endRecording();
@ -856,9 +869,11 @@ static void usage(const char * argv0) {
" any differences between those and the newly generated ones\n"
" [--noreplay]: do not exercise SkPicture replay\n"
" [--resourcePath|-i <path>]: directory that stores image resources\n"
" [--rtree]: use an rtree structure for SkPicture testing\n"
" [--noserialize]: do not exercise SkPicture serialization & deserialization\n"
" [--notexturecache]: disable the gpu texture cache\n"
" [--tiledPipe]: Exercise tiled SkGPipe replay\n"
" [--tileGrid]: use a tileGrid structure for SkPicture testing\n"
" [--writePath|-w <path>]: write rendered images into this directory\n"
" [--writePicturePath|-wp <path>]: write .skp files into this directory\n"
);
@ -959,6 +974,7 @@ int tool_main(int argc, char** argv) {
bool disableTextureCache = false;
SkTDArray<size_t> configs;
bool userConfig = false;
BbhType bbhType = kNone_BbhType;
int moduloRemainder = -1;
int moduloDivisor = -1;
@ -995,6 +1011,10 @@ int tool_main(int argc, char** argv) {
}
} else if (strcmp(*argv, "--disable-missing-warning") == 0) {
gmmain.fNotifyMissingReadReference = false;
} else if (strcmp(*argv, "--rtree") == 0) {
bbhType = kRTree_BbhType;
} else if (strcmp(*argv, "--tileGrid") == 0) {
bbhType = kTileGrid_BbhType;
} else if (strcmp(*argv, "--enable-missing-warning") == 0) {
gmmain.fNotifyMissingReadReference = true;
} else if (strcmp(*argv, "--forceBWtext") == 0) {
@ -1246,7 +1266,7 @@ int tool_main(int argc, char** argv) {
ErrorBitfield pictErrors = ERROR_NONE;
//SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
SkPicture* pict = gmmain.generate_new_picture(gm);
SkPicture* pict = gmmain.generate_new_picture(gm, bbhType);
SkAutoUnref aur(pict);
if ((ERROR_NONE == testErrors) && doReplay) {

View File

@ -164,6 +164,7 @@
'<(skia_src_path)/core/SkTextFormatParams.h',
'<(skia_src_path)/core/SkTileGrid.cpp',
'<(skia_src_path)/core/SkTileGrid.h',
'<(skia_src_path)/core/SkTileGridPicture.cpp',
'<(skia_src_path)/core/SkTLList.h',
'<(skia_src_path)/core/SkTLS.cpp',
'<(skia_src_path)/core/SkTSearch.cpp',
@ -255,6 +256,7 @@
'<(skia_include_path)/core/SkTDStack.h',
'<(skia_include_path)/core/SkTDict.h',
'<(skia_include_path)/core/SkTInternalLList.h',
'<(skia_include_path)/core/SkTileGridPicture.h',
'<(skia_include_path)/core/SkTRegistry.h',
'<(skia_include_path)/core/SkTScopedPtr.h',
'<(skia_include_path)/core/SkTSearch.h',

View File

@ -0,0 +1,29 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkTileGridPicture_DEFINED
#define SkTileGridPicture_DEFINED
#include "SkPicture.h"
/**
* Subclass of SkPicture that override the behavior of the
* kOptimizeForClippedPlayback_RecordingFlag by creating an SkTileGrid
* structure rather than an R-Tree. The tile grid has lower recording
* and playback costs, but is less effective at eliminating extraneous
* primitives for arbitrary query rectangles. It is most effective for
* tiled playback when the tile structure is known at record time.
*/
class SkTileGridPicture : public SkPicture {
public:
SkTileGridPicture(int tileWidth, int tileHeight, int width, int height);
virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE;
private:
int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
};
#endif

View File

@ -8,7 +8,7 @@
#include "SkTileGrid.h"
SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount)
SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, SkTileGridNextDatumFunctionPtr nextDatumFunction)
{
fTileWidth = tileWidth;
fTileHeight = tileHeight;
@ -17,7 +17,7 @@ SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileC
fTileCount = fXTileCount * fYTileCount;
fInsertionCount = 0;
fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount);
fNextDatumFunction = nextDatumFunction;
fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount);
}
@ -52,12 +52,42 @@ void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) {
}
void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) {
SkASSERT(query.width() == fTileWidth + 2 && query.height() == fTileHeight + 2);
SkASSERT((query.left() + 1) % fTileWidth == 0);
SkASSERT((query.top() + 1) % fTileHeight == 0);
SkASSERT(results);
// The +1 is to compensate for the outset in applied SkCanvas::getClipBounds
*results = this->tile((query.left() + 1) / fTileWidth, (query.top() + 1) / fTileHeight);
// The +1/-1 is to compensate for the outset in applied SkCanvas::getClipBounds
int tileStartX = (query.left() + 1) / fTileWidth;
int tileEndX = (query.right() + fTileWidth - 1) / fTileWidth;
int tileStartY = (query.top() + 1) / fTileHeight;
int tileEndY = (query.bottom() + fTileHeight - 1) / fTileHeight;
if (tileStartX >= fXTileCount || tileStartY >= fYTileCount || tileEndX <= 0 || tileEndY <= 0) {
return; // query does not intersect the grid
}
// clamp to grid
if (tileStartX < 0) tileStartX = 0;
if (tileStartY < 0) tileStartY = 0;
if (tileEndX > fXTileCount) tileEndX = fXTileCount;
if (tileEndY > fYTileCount) tileEndY = fYTileCount;
int queryTileCount = (tileEndX - tileStartX) * (tileEndY - tileStartY);
if (queryTileCount == 1) {
*results = this->tile(tileStartX, tileStartY);
} else {
results->reset();
SkTDArray<int> curPositions;
curPositions.setCount(queryTileCount);
SkTDArray<void *>** tileRange =
(SkTDArray<void *>**)alloca(queryTileCount * sizeof(SkTDArray<void *>*));
int tile = 0;
for (int x = tileStartX; x < tileEndX; ++x) {
for (int y = tileStartY; y < tileEndY; ++y) {
tileRange[tile] = &this->tile(x, y);
curPositions[tile] = tileRange[tile]->count() ? 0 : kTileFinished;
++tile;
}
}
void *nextElement;
while(NULL != (nextElement = fNextDatumFunction(tileRange, curPositions))) {
results->push(nextElement);
}
}
}
void SkTileGrid::clear() {
@ -68,6 +98,4 @@ void SkTileGrid::clear() {
int SkTileGrid::getCount() const {
return fInsertionCount;
}
}

View File

@ -10,6 +10,7 @@
#define SkTileGrid_DEFINED
#include "SkBBoxHierarchy.h"
#include "SkPictureStateTree.h"
/**
* Subclass of SkBBoxHierarchy that stores elements in buckets that correspond
@ -23,7 +24,10 @@
*/
class SkTileGrid : public SkBBoxHierarchy {
public:
SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount);
typedef void* (*SkTileGridNextDatumFunctionPtr)(SkTDArray<void*>** tileData, SkTDArray<int>& tileIndices);
SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount,
SkTileGridNextDatumFunctionPtr nextDatumFunction);
virtual ~SkTileGrid();
@ -50,17 +54,67 @@ public:
*/
virtual int getCount() const SK_OVERRIDE;
// Used by search() and in SkTileGridHelper implementations
enum {
kTileFinished = -1,
};
private:
SkTDArray<void *>& tile(int x, int y);
SkTDArray<void*>& tile(int x, int y);
int fTileWidth, fTileHeight, fXTileCount, fYTileCount, fTileCount;
SkTDArray<void *> *fTileData;
SkTDArray<void*>* fTileData;
int fInsertionCount;
SkIRect fGridBounds;
SkTileGridNextDatumFunctionPtr fNextDatumFunction;
friend class TileGridTest;
typedef SkBBoxHierarchy INHERITED;
};
#endif
/**
* Generic implementation for SkTileGridNextDatumFunctionPtr. user code may instantiate
* this template to get a valid SkTileGridNextDatumFunction implementation
*
* Returns the next element of tileData[i][tileIndices[i]] for all i and advances
* tileIndices[] past them. The order in which data are returned by successive
* calls to this method must reflect the order in which the were originally
* recorded into the tile grid.
*
* \param tileData array of pointers to arrays of tile data
* \param tileIndices per-tile data indices, indices are incremented for tiles that contain
* the next datum.
* \tparam T a type to which it is safe to cast a datum and that has an operator <
* such that 'a < b' is true if 'a' was inserted into the tile grid before 'b'.
*/
template <typename T>
void* SkTileGridNextDatum(SkTDArray<void*>** tileData, SkTDArray<int>& tileIndices) {
bool haveVal = false;
T* minVal;
int tileCount = tileIndices.count();
// Find the next Datum
for (int tile = 0; tile < tileCount; ++tile) {
int pos = tileIndices[tile];
if (pos != SkTileGrid::kTileFinished) {
T* candidate = (T*)(*tileData[tile])[pos];
if (!haveVal || (*candidate) < (*minVal)) {
minVal = candidate;
haveVal = true;
}
}
}
// Increment indices past the next datum
if (haveVal) {
for (int tile = 0; tile < tileCount; ++tile) {
int pos = tileIndices[tile];
if (pos != SkTileGrid::kTileFinished && (*tileData[tile])[pos] == minVal) {
if (++(tileIndices[tile]) >= tileData[tile]->count()) {
tileIndices[tile] = SkTileGrid::kTileFinished;
}
}
}
return minVal;
}
return NULL;
}
#endif

View File

@ -0,0 +1,24 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTileGridPicture.h"
#include "SkPictureStateTree.h"
#include "SkTileGrid.h"
SkTileGridPicture::SkTileGridPicture(int tileWidth, int tileHeight, int width, int height) {
fTileWidth = tileWidth;
fTileHeight = tileHeight;
fXTileCount = (width + tileWidth - 1) / tileWidth;
fYTileCount = (height + tileHeight - 1) / tileHeight;
}
SkBBoxHierarchy* SkTileGridPicture::createBBoxHierarchy() const {
return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount,
SkTileGridNextDatum<SkPictureStateTree::Draw>));
}

View File

@ -8,6 +8,9 @@
#include "Test.h"
#include "SkTileGrid.h"
#include "SkTileGridPicture.h"
#include "SkCanvas.h"
#include "SkDevice.h"
enum Tile {
kTopLeft_Tile = 0x1,
@ -18,10 +21,26 @@ enum Tile {
kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile,
};
namespace {
class MockCanvas : public SkCanvas {
public:
MockCanvas(SkDevice* device) : SkCanvas(device)
{}
virtual void drawRect(const SkRect& rect, const SkPaint& paint)
{
// This capture occurs before quick reject.
fRects.push(rect);
}
SkTDArray<SkRect> fRects;
};
}
class TileGridTest {
public:
static void verifyTileHits(skiatest::Reporter* reporter, SkIRect rect, uint32_t tileMask) {
SkTileGrid grid(10, 10, 2, 2);
SkTileGrid grid(10, 10, 2, 2, NULL);
grid.insert(NULL, rect, false);
REPORTER_ASSERT(reporter, grid.tile(0,0).count() ==
((tileMask & kTopLeft_Tile)? 1 : 0));
@ -33,6 +52,60 @@ public:
((tileMask & kBottomRight_Tile)? 1 : 0));
}
static void TestUnalignedQuery(skiatest::Reporter* reporter) {
// Use SkTileGridPicture to generate a SkTileGrid with a helper
SkTileGridPicture picture(10, 10, 20, 20);
SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
SkIntToScalar(8), SkIntToScalar(8));
SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11),
SkIntToScalar(1), SkIntToScalar(1));
SkCanvas* canvas = picture.beginRecording(20, 20, SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
SkPaint paint;
canvas->drawRect(rect1, paint);
canvas->drawRect(rect2, paint);
picture.endRecording();
SkBitmap store;
store.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
store.allocPixels();
// Test parts of top-left tile
{
SkDevice device(store);
MockCanvas mockCanvas(&device);
picture.draw(&mockCanvas);
REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
}
{
SkDevice device(store);
MockCanvas mockCanvas(&device);
mockCanvas.translate(SkFloatToScalar(-7.99f), SkFloatToScalar(-7.99f));
picture.draw(&mockCanvas);
REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
}
// Corner overlap
{
SkDevice device(store);
MockCanvas mockCanvas(&device);
mockCanvas.translate(SkFloatToScalar(-9.5f), SkFloatToScalar(-9.5f));
picture.draw(&mockCanvas);
REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
}
// Intersect bottom right tile, but does not overlap rect 2
{
SkDevice device(store);
MockCanvas mockCanvas(&device);
mockCanvas.translate(SkFloatToScalar(-16.0f), SkFloatToScalar(-16.0f));
picture.draw(&mockCanvas);
REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
}
}
static void Test(skiatest::Reporter* reporter) {
// Out of bounds
verifyTileHits(reporter, SkIRect::MakeXYWH(30, 0, 1, 1), 0);
@ -52,6 +125,8 @@ public:
kBottomLeft_Tile);
verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 10), kAll_Tile);
verifyTileHits(reporter, SkIRect::MakeXYWH(-10, -10, 40, 40), kAll_Tile);
TestUnalignedQuery(reporter);
}
};

View File

@ -24,7 +24,7 @@
#include "SkStream.h"
#include "SkString.h"
#include "SkTemplates.h"
#include "SkTileGrid.h"
#include "SkTileGridPicture.h"
#include "SkTDArray.h"
#include "SkThreadUtils.h"
#include "SkTypes.h"
@ -624,22 +624,6 @@ public:
}
};
class TileGridPicture : public SkPicture {
public:
TileGridPicture(int tileWidth, int tileHeight, int xTileCount, int yTileCount) {
fTileWidth = tileWidth;
fTileHeight = tileHeight;
fXTileCount = xTileCount;
fYTileCount = yTileCount;
}
virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount));
}
private:
int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
};
SkPicture* PictureRenderer::createPicture() {
switch (fBBoxHierarchyType) {
case kNone_BBoxHierarchyType:
@ -647,14 +631,8 @@ SkPicture* PictureRenderer::createPicture() {
case kRTree_BBoxHierarchyType:
return SkNEW(RTreePicture);
case kTileGrid_BBoxHierarchyType:
{
int xTileCount = fPicture->width() / fGridWidth +
((fPicture->width() % fGridWidth) ? 1 : 0);
int yTileCount = fPicture->height() / fGridHeight +
((fPicture->height() % fGridHeight) ? 1 : 0);
return SkNEW_ARGS(TileGridPicture, (fGridWidth, fGridHeight, xTileCount,
yTileCount));
}
return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
fPicture->height()));
}
SkASSERT(0); // invalid bbhType
return NULL;