diff options
-rw-r--r-- | gyp/tests.gypi | 1 | ||||
-rw-r--r-- | src/core/SkPicturePlayback.cpp | 6 | ||||
-rw-r--r-- | src/core/SkPictureStateTree.cpp | 60 | ||||
-rw-r--r-- | src/core/SkPictureStateTree.h | 8 | ||||
-rw-r--r-- | tests/PictureStateTreeTest.cpp | 123 |
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); +} |