skia2/tests/PictureBBHTest.cpp
Mike Klein 738b80d8a5 Don't use getDeviceClipBounds() to bound pic ops.
The values returned by SkCanvas::getDeviceClipBounds() are in the right
space, but have extra constraints on them that are not desirable for
bounding the logical bounds of draw operations:

  - they are integral
  - they are non-negative

We've been intersecting the bounds of each operation with these bounds,
which means we're mixing these bogus constraints into the bounds of each
recorded operation.  This percolates up to the SkPicutre cull rect too.

The most egregious way to see the problem is to record a draw op
entirely in negative space... it'll come back with empty logical bounds
rather than its correct (negative-space) bounds.  I've added a test
for this, and another test I also think should be passing but left
making it so as a follow up.

I've had to disable a couple tests asserting clips affect the bounds. :/

A possible follow-up might go back to using the clips to tighten the
bounds of the ops, just so long as we take the original user bounds and
map them with the CTM through to device space ourselves, rather than
relying on the recording canvas' clip stack.  I think this means we'd
need to maintain our own stack of device-space float SkRect clip bounds
while calculating these op bounds.

Bug: skia:7735
Change-Id: I6bf15f6b2a9ba4329a4eeae7f9d57aa8729ec1bb
Reviewed-on: https://skia-review.googlesource.com/126002
Commit-Queue: Mike Klein <mtklein@chromium.org>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: Mike Klein <mtklein@chromium.org>
Reviewed-by: Brian Osman <brianosman@google.com>
2018-05-04 18:56:13 +00:00

138 lines
4.3 KiB
C++

/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkBBoxHierarchy.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkRectPriv.h"
#include "Test.h"
class PictureBBHTestBase {
public:
PictureBBHTestBase(int playbackWidth, int playbackHeight,
int recordWidth, int recordHeight) {
fResultBitmap.allocN32Pixels(playbackWidth, playbackHeight);
fPictureWidth = recordWidth;
fPictureHeight = recordHeight;
}
virtual ~PictureBBHTestBase() { }
virtual void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) = 0;
void run(skiatest::Reporter* reporter) {
// No BBH
this->run(nullptr, reporter);
// With an R-Tree
SkRTreeFactory RTreeFactory;
this->run(&RTreeFactory, reporter);
}
private:
void run(SkBBHFactory* factory, skiatest::Reporter* reporter) {
SkCanvas playbackCanvas(fResultBitmap);
playbackCanvas.clear(SK_ColorGREEN);
SkPictureRecorder recorder;
SkCanvas* recordCanvas = recorder.beginRecording(SkIntToScalar(fPictureWidth),
SkIntToScalar(fPictureHeight),
factory);
this->doTest(playbackCanvas, *recordCanvas);
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
playbackCanvas.drawPicture(picture);
REPORTER_ASSERT(reporter, SK_ColorGREEN == fResultBitmap.getColor(0, 0));
}
SkBitmap fResultBitmap;
int fPictureWidth, fPictureHeight;
};
// Test to verify the playback of an empty picture
//
class DrawEmptyPictureBBHTest : public PictureBBHTestBase {
public:
DrawEmptyPictureBBHTest()
: PictureBBHTestBase(2, 2, 1, 1) {}
~DrawEmptyPictureBBHTest() override {}
void doTest(SkCanvas&, SkCanvas&) override {}
};
// Test to verify the playback of a picture into a canvas that has
// an empty clip.
//
class EmptyClipPictureBBHTest : public PictureBBHTestBase {
public:
EmptyClipPictureBBHTest()
: PictureBBHTestBase(2, 2, 3, 3) {}
void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) override {
// intersect with out of bounds rect -> empty clip.
playbackCanvas.clipRect(SkRect::MakeXYWH(10, 10, 1, 1));
SkPaint paint;
recordingCanvas.drawRect(SkRect::MakeWH(3, 3), paint);
}
~EmptyClipPictureBBHTest() override {}
};
DEF_TEST(PictureBBH, reporter) {
DrawEmptyPictureBBHTest emptyPictureTest;
emptyPictureTest.run(reporter);
EmptyClipPictureBBHTest emptyClipPictureTest;
emptyClipPictureTest.run(reporter);
}
DEF_TEST(RTreeMakeLargest, r) {
// A call to insert() with 2 or more rects and a bounds of SkRect::MakeLargest()
// used to fall into an infinite loop.
SkRTreeFactory factory;
std::unique_ptr<SkBBoxHierarchy> bbh{ factory(SkRectPriv::MakeLargest()) };
SkRect rects[] = { {0,0, 10,10}, {5,5,15,15} };
bbh->insert(rects, SK_ARRAY_COUNT(rects));
REPORTER_ASSERT(r, bbh->getRootBound() == SkRect::MakeWH(15,15));
}
DEF_TEST(PictureNegativeSpace, r) {
SkRTreeFactory factory;
SkPictureRecorder recorder;
SkRect cull = {-200,-200,+200,+200};
{
auto canvas = recorder.beginRecording(cull, &factory);
canvas->save();
canvas->clipRect(cull);
canvas->drawRect({-20,-20,-10,-10}, SkPaint{});
canvas->drawRect({-20,-20,-10,-10}, SkPaint{});
canvas->restore();
auto pic = recorder.finishRecordingAsPicture();
REPORTER_ASSERT(r, pic->approximateOpCount() == 5);
REPORTER_ASSERT(r, pic->cullRect() == (SkRect{-20,-20,-10,-10}));
}
// TODO: we should also get the same results without the explicit save/restore
if (0) {
auto canvas = recorder.beginRecording(cull, &factory);
canvas->clipRect(cull);
canvas->drawRect({-20,-20,-10,-10}, SkPaint{});
canvas->drawRect({-20,-20,-10,-10}, SkPaint{});
auto pic = recorder.finishRecordingAsPicture();
REPORTER_ASSERT(r, pic->approximateOpCount() == 3);
REPORTER_ASSERT(r, pic->cullRect() == (SkRect{-20,-20,-10,-10}));
}
}