aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/tests.gypi1
-rw-r--r--src/core/SkPicturePlayback.cpp6
-rw-r--r--src/core/SkPictureStateTree.cpp60
-rw-r--r--src/core/SkPictureStateTree.h8
-rw-r--r--tests/PictureStateTreeTest.cpp123
5 files changed, 166 insertions, 32 deletions
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index f27b144dea..982d58d7cd 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -129,6 +129,7 @@
'../tests/PathUtilsTest.cpp',
'../tests/PictureTest.cpp',
'../tests/PictureShaderTest.cpp',
+ '../tests/PictureStateTreeTest.cpp',
'../tests/PictureUtilsTest.cpp',
'../tests/PixelRefTest.cpp',
'../tests/PointTest.cpp',
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index bd5244d223..7d4c843083 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -851,7 +851,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
fStateTree->getIterator(*activeOps, &canvas);
if (it.isValid()) {
- uint32_t skipTo = it.draw();
+ uint32_t skipTo = it.nextDraw();
if (kDrawComplete == skipTo) {
return;
}
@@ -907,7 +907,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
// iterator until at or after skipTo
uint32_t adjustedSkipTo;
do {
- adjustedSkipTo = it.draw();
+ adjustedSkipTo = it.nextDraw();
} while (adjustedSkipTo < skipTo);
skipTo = adjustedSkipTo;
}
@@ -1246,7 +1246,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
#endif
if (it.isValid()) {
- uint32_t skipTo = it.draw();
+ uint32_t skipTo = it.nextDraw();
if (kDrawComplete == skipTo) {
break;
}
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
index 21ae3cb9f0..f6d54ffedc 100644
--- a/src/core/SkPictureStateTree.cpp
+++ b/src/core/SkPictureStateTree.cpp
@@ -99,24 +99,41 @@ SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas*
, fValid(true) {
}
-uint32_t SkPictureStateTree::Iterator::draw() {
+void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) {
+ SkASSERT(NULL != matrix);
+
+ if (matrix == fCurrentMatrix) {
+ return;
+ }
+
+ // The matrix is in recording space, but we also inherit
+ // a playback matrix from out target canvas.
+ SkMatrix m = *matrix;
+ m.postConcat(fPlaybackMatrix);
+ fCanvas->setMatrix(m);
+ fCurrentMatrix = matrix;
+}
+
+uint32_t SkPictureStateTree::Iterator::nextDraw() {
SkASSERT(this->isValid());
if (fPlaybackIndex >= fDraws->count()) {
- // restore back to where we started
- fCanvas->setMatrix(fPlaybackMatrix);
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
fCanvas->restore();
}
- fCurrentNode = fCurrentNode->fParent;
- while (NULL != fCurrentNode) {
+
+ for (fCurrentNode = fCurrentNode->fParent; fCurrentNode;
+ fCurrentNode = fCurrentNode->fParent) {
+ // Note: we call restore() twice when both flags are set.
if (fCurrentNode->fFlags & Node::kSave_Flag) {
fCanvas->restore();
}
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
fCanvas->restore();
}
- fCurrentNode = fCurrentNode->fParent;
}
+
+ fCanvas->setMatrix(fPlaybackMatrix);
+ fCurrentMatrix = NULL;
return kDrawComplete;
}
@@ -124,10 +141,7 @@ uint32_t SkPictureStateTree::Iterator::draw() {
Node* targetNode = draw->fNode;
if (fSave) {
- // FIXME: the save below depends on soon-to-be-deprecated
- // SaveFlags behavior: it relies on matrix changes persisting
- // after restore.
- fCanvas->save(SkCanvas::kClip_SaveFlag);
+ fCanvas->save();
fSave = false;
}
@@ -148,9 +162,13 @@ uint32_t SkPictureStateTree::Iterator::draw() {
if (currentLevel >= targetLevel) {
if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) {
fCanvas->restore();
+ // restore() may change the matrix, so we need to reapply.
+ fCurrentMatrix = NULL;
}
if (tmp->fFlags & Node::kSaveLayer_Flag) {
fCanvas->restore();
+ // restore() may change the matrix, so we need to reapply.
+ fCurrentMatrix = NULL;
}
tmp = tmp->fParent;
}
@@ -163,12 +181,11 @@ uint32_t SkPictureStateTree::Iterator::draw() {
if (ancestor->fFlags & Node::kSave_Flag) {
if (fCurrentNode != ancestor) {
fCanvas->restore();
+ // restore() may change the matrix, so we need to reapply.
+ fCurrentMatrix = NULL;
}
if (targetNode != ancestor) {
- // FIXME: the save below depends on soon-to-be-deprecated
- // SaveFlags behavior: it relies on matrix changes persisting
- // after restore.
- fCanvas->save(SkCanvas::kClip_SaveFlag);
+ fCanvas->save();
}
}
fCurrentNode = ancestor;
@@ -177,29 +194,18 @@ uint32_t SkPictureStateTree::Iterator::draw() {
// If we're not at the target node yet, we'll need to return an offset to make the caller
// apply the next clip or saveLayer.
if (fCurrentNode != targetNode) {
- if (fCurrentMatrix != fNodes.top()->fMatrix) {
- fCurrentMatrix = fNodes.top()->fMatrix;
- SkMatrix tmp = *fNodes.top()->fMatrix;
- tmp.postConcat(fPlaybackMatrix);
- fCanvas->setMatrix(tmp);
- }
uint32_t offset = fNodes.top()->fOffset;
fCurrentNode = fNodes.top();
fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
fNodes.pop();
+ this->setCurrentMatrix(fCurrentNode->fMatrix);
return offset;
}
}
// If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
// for the draw, and return its offset.
-
- if (fCurrentMatrix != draw->fMatrix) {
- SkMatrix tmp = *draw->fMatrix;
- tmp.postConcat(fPlaybackMatrix);
- fCanvas->setMatrix(tmp);
- fCurrentMatrix = draw->fMatrix;
- }
+ this->setCurrentMatrix(draw->fMatrix);
++fPlaybackIndex;
return draw->fOffset;
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
index 448c0d31ce..d61bf032cb 100644
--- a/src/core/SkPictureStateTree.h
+++ b/src/core/SkPictureStateTree.h
@@ -75,12 +75,16 @@ public:
class Iterator {
public:
/** Returns the next offset into the picture stream, or kDrawComplete if complete. */
- uint32_t draw();
+ uint32_t nextDraw();
static const uint32_t kDrawComplete = SK_MaxU32;
Iterator() : fPlaybackMatrix(), fValid(false) { }
bool isValid() const { return fValid; }
+
private:
Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
+
+ void setCurrentMatrix(const SkMatrix*);
+
// The draws this iterator is associated with
const SkTDArray<void*>* fDraws;
@@ -97,7 +101,7 @@ public:
const SkMatrix fPlaybackMatrix;
// Cache of current matrix, so we can avoid redundantly setting it
- SkMatrix* fCurrentMatrix;
+ const SkMatrix* fCurrentMatrix;
// current position in the array of draws
int fPlaybackIndex;
diff --git a/tests/PictureStateTreeTest.cpp b/tests/PictureStateTreeTest.cpp
new file mode 100644
index 0000000000..e6cf55342e
--- /dev/null
+++ b/tests/PictureStateTreeTest.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "SkBBHFactory.h"
+#include "SkCanvas.h"
+#include "SkPictureRecorder.h"
+#include "SkPictureStateTree.h"
+#include "Test.h"
+
+static SkPicture* draw_scene(SkBBHFactory* bbhFactory) {
+ SkPictureRecorder recorder;
+ SkCanvas* canvas = recorder.beginRecording(200, 200, bbhFactory, 0);
+
+ SkPaint p1, p2;
+ p1.setStyle(SkPaint::kFill_Style);
+ p1.setARGB(0x80, 0, 0xff, 0);
+ p2.setStyle(SkPaint::kFill_Style);
+ p2.setARGB(0x80, 0xff, 0, 0);
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ // This is intended to exercise some tricky SkPictureStateTree code paths when
+ // played back with various clips:
+ //
+ // * cleanup/rewind when the last draw is not at the root of the state tree.
+ // * state nodes with both kSave_Flag & kSaveLayer_Flag set.
+ // * state tree transitions which implicitly reset the matrix via. restore().
+
+ canvas->save();
+ canvas->translate(10, 10);
+
+ canvas->drawRect(SkRect::MakeWH(100, 50), p1);
+ canvas->drawRect(SkRect::MakeWH(50, 100), p2);
+
+ SkRect layerBounds = SkRect::MakeXYWH(0, 0, 90, 90);
+ canvas->saveLayer(&layerBounds, NULL);
+ canvas->save();
+ canvas->clipRect(layerBounds);
+
+ canvas->save();
+ canvas->clipRect(SkRect::MakeWH(25, 25));
+ canvas->rotate(90);
+ canvas->drawRect(SkRect::MakeWH(100, 50), p1);
+ canvas->restore();
+
+ canvas->save();
+ canvas->clipRect(SkRect::MakeWH(25, 25));
+ canvas->save();
+ canvas->rotate(90);
+ canvas->drawRect(SkRect::MakeWH(50, 100), p2);
+ canvas->restore();
+ canvas->drawRect(SkRect::MakeWH(100, 50), p1);
+ canvas->restore();
+ canvas->drawRect(SkRect::MakeXYWH(99, 99, 1, 1), p1);
+ canvas->restore();
+ canvas->restore();
+
+ canvas->restore();
+
+ return recorder.endRecording();
+}
+
+static void check_bms(skiatest::Reporter* reporter, const SkBitmap& bm1, const SkBitmap& bm2) {
+ SkASSERT(bm1.getSize() == bm2.getSize());
+ REPORTER_ASSERT(reporter, 0 == memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), bm1.getSize()));
+}
+
+static void test_reference_picture(skiatest::Reporter* reporter) {
+ SkRTreeFactory bbhFactory;
+
+ SkAutoTUnref<SkPicture> bbhPicture(draw_scene(&bbhFactory));
+ SkAutoTUnref<SkPicture> referencePicture(draw_scene(NULL));
+
+ SkBitmap referenceBitmap;
+ referenceBitmap.allocN32Pixels(100, 100);
+ SkCanvas referenceCanvas(referenceBitmap);
+
+ SkBitmap bbhBitmap;
+ bbhBitmap.allocN32Pixels(100, 100);
+ SkCanvas bbhCanvas(bbhBitmap);
+
+ referenceCanvas.drawColor(SK_ColorTRANSPARENT);
+ referenceCanvas.drawPicture(*referencePicture.get());
+ bbhCanvas.drawColor(SK_ColorTRANSPARENT);
+ bbhCanvas.drawPicture(*bbhPicture.get());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
+ check_bms(reporter, referenceBitmap, bbhBitmap);
+
+ referenceCanvas.drawColor(SK_ColorTRANSPARENT);
+ referenceCanvas.clipRect(SkRect::MakeWH(50, 50));
+ referenceCanvas.drawPicture(*referencePicture.get());
+ bbhCanvas.drawColor(SK_ColorTRANSPARENT);
+ bbhCanvas.clipRect(SkRect::MakeWH(50, 50));
+ bbhCanvas.drawPicture(*bbhPicture.get());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
+ check_bms(reporter, referenceBitmap, bbhBitmap);
+
+ referenceCanvas.drawColor(SK_ColorTRANSPARENT);
+ referenceCanvas.clipRect(SkRect::MakeWH(10, 10));
+ referenceCanvas.drawPicture(*referencePicture.get());
+ bbhCanvas.drawColor(SK_ColorTRANSPARENT);
+ bbhCanvas.clipRect(SkRect::MakeWH(10, 10));
+ bbhCanvas.drawPicture(*bbhPicture.get());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getSaveCount() == bbhCanvas.getSaveCount());
+ REPORTER_ASSERT(reporter,
+ referenceCanvas.getTotalMatrix() == bbhCanvas.getTotalMatrix());
+ check_bms(reporter, referenceBitmap, bbhBitmap);
+}
+
+DEF_TEST(PictureStateTree, reporter) {
+ test_reference_picture(reporter);
+}