/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Description: * This test defines a series of elementatry test steps that perform * a single or a small group of canvas API calls. Each test step is * used in several test cases that verify that different types of SkCanvas * flavors and derivatives pass it and yield consistent behavior. The * test cases analyse results that are queryable through the API. They do * not look at rendering results. * * Adding test stepss: * The general pattern for creating a new test step is to write a test * function of the form: * * static void MyTestStepFunction(SkCanvas* canvas, * skiatest::Reporter* reporter, * CanvasTestStep* testStep) * { * canvas->someCanvasAPImethod(); * (...) * REPORTER_ASSERT_MESSAGE(reporter, (...), \ * testStep->assertMessage()); * } * * The definition of the test step function should be followed by an * invocation of the TEST_STEP macro, which generates a class and * instance for the test step: * * TEST_STEP(MyTestStep, MyTestStepFunction) * * There are also short hand macros for defining simple test steps * in a single line of code. A simple test step is a one that is made * of a single canvas API call. * * SIMPLE_TEST_STEP(MytestStep, someCanvasAPIMethod()); * * There is another macro called SIMPLE_TEST_STEP_WITH_ASSERT that * works the same way as SIMPLE_TEST_STEP, and additionally verifies * that the invoked method returns a non-zero value. */ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkDeferredCanvas.h" #include "SkDevice.h" #include "SkMatrix.h" #include "SkNWayCanvas.h" #include "SkPDFDevice.h" #include "SkPDFDocument.h" #include "SkPaint.h" #include "SkPath.h" #include "SkPicture.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" #include "SkProxyCanvas.h" #include "SkRect.h" #include "SkRegion.h" #include "SkShader.h" #include "SkStream.h" #include "SkSurface.h" #include "SkTDArray.h" #include "Test.h" static bool equal_clips(const SkCanvas& a, const SkCanvas& b) { if (a.isClipEmpty()) { return b.isClipEmpty(); } if (!a.isClipRect()) { // this is liberally true, since we don't expose a way to know this exactly (for non-rects) return !b.isClipRect(); } SkIRect ar, br; a.getClipDeviceBounds(&ar); b.getClipDeviceBounds(&br); return ar == br; } class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor { public: Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {} virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { fTarget->clipRect(r, op, aa); } virtual void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { fTarget->clipRRect(r, op, aa); } virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) SK_OVERRIDE { fTarget->clipPath(p, op, aa); } private: SkCanvas* fTarget; }; static void test_clipVisitor(skiatest::Reporter* reporter, SkCanvas* canvas) { SkISize size = canvas->getDeviceSize(); SkBitmap bm; bm.setInfo(SkImageInfo::MakeN32Premul(size.width(), size.height())); SkCanvas c(bm); Canvas2CanvasClipVisitor visitor(&c); canvas->replayClips(&visitor); REPORTER_ASSERT(reporter, equal_clips(c, *canvas)); } static const int kWidth = 2; static const int kHeight = 2; // Format strings that describe the test context. The %s token is where // the name of the test step is inserted. The context is required for // disambiguating the error in the case of failures that are reported in // functions that are called multiple times in different contexts (test // cases and test steps). static const char* const kDefaultAssertMessageFormat = "%s"; static const char* const kCanvasDrawAssertMessageFormat = "Drawing test step %s with SkCanvas"; static const char* const kPictureDrawAssertMessageFormat = "Drawing test step %s with SkPicture"; static const char* const kPictureSecondDrawAssertMessageFormat = "Duplicate draw of test step %s with SkPicture"; static const char* const kDeferredDrawAssertMessageFormat = "Drawing test step %s with SkDeferredCanvas"; static const char* const kProxyDrawAssertMessageFormat = "Drawing test step %s with SkProxyCanvas"; static const char* const kNWayDrawAssertMessageFormat = "Drawing test step %s with SkNWayCanvas"; static const char* const kDeferredPreFlushAssertMessageFormat = "test step %s, SkDeferredCanvas state consistency before flush"; static const char* const kDeferredPostFlushPlaybackAssertMessageFormat = "test step %s, SkDeferredCanvas playback canvas state consistency after flush"; static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat = "test step %s, SkDeferredCanvas playback canvas state consistency after silent flush"; static const char* const kPictureResourceReuseMessageFormat = "test step %s, SkPicture duplicate flattened object test"; static const char* const kProxyStateAssertMessageFormat = "test step %s, SkProxyCanvas state consistency"; static const char* const kProxyIndirectStateAssertMessageFormat = "test step %s, SkProxyCanvas indirect canvas state consistency"; static const char* const kNWayStateAssertMessageFormat = "test step %s, SkNWayCanvas state consistency"; static const char* const kNWayIndirect1StateAssertMessageFormat = "test step %s, SkNWayCanvas indirect canvas 1 state consistency"; static const char* const kNWayIndirect2StateAssertMessageFormat = "test step %s, SkNWayCanvas indirect canvas 2 state consistency"; static const char* const kPdfAssertMessageFormat = "PDF sanity check failed %s"; static void createBitmap(SkBitmap* bm, SkColor color) { bm->allocN32Pixels(kWidth, kHeight); bm->eraseColor(color); } static SkSurface* createSurface(SkColor color) { SkSurface* surface = SkSurface::NewRasterPMColor(kWidth, kHeight); surface->getCanvas()->clear(color); return surface; } class CanvasTestStep; static SkTDArray<CanvasTestStep*>& testStepArray() { static SkTDArray<CanvasTestStep*> theTests; return theTests; } class CanvasTestStep { public: CanvasTestStep(bool fEnablePdfTesting = true) { *testStepArray().append() = this; fAssertMessageFormat = kDefaultAssertMessageFormat; this->fEnablePdfTesting = fEnablePdfTesting; } virtual ~CanvasTestStep() { } virtual void draw(SkCanvas*, skiatest::Reporter*) = 0; virtual const char* name() const = 0; const char* assertMessage() { fAssertMessage.printf(fAssertMessageFormat, name()); return fAssertMessage.c_str(); } void setAssertMessageFormat(const char* format) { fAssertMessageFormat = format; } bool enablePdfTesting() { return fEnablePdfTesting; } private: SkString fAssertMessage; const char* fAssertMessageFormat; bool fEnablePdfTesting; }; /////////////////////////////////////////////////////////////////////////////// // Constants used by test steps const SkRect kTestRect = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1)); static SkMatrix testMatrix() { SkMatrix matrix; matrix.reset(); matrix.setScale(SkIntToScalar(2), SkIntToScalar(3)); return matrix; } const SkMatrix kTestMatrix = testMatrix(); static SkPath test_path() { SkPath path; path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2), SkIntToScalar(1))); return path; } const SkPath kTestPath = test_path(); static SkPath test_nearly_zero_length_path() { SkPath path; SkPoint pt1 = { 0, 0 }; SkPoint pt2 = { 0, SK_ScalarNearlyZero }; SkPoint pt3 = { SkIntToScalar(1), 0 }; SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 }; path.moveTo(pt1); path.lineTo(pt2); path.lineTo(pt3); path.lineTo(pt4); return path; } const SkPath kNearlyZeroLengthPath = test_nearly_zero_length_path(); static SkRegion testRegion() { SkRegion region; SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1); region.setRect(rect); return region; } const SkIRect kTestIRect = SkIRect::MakeXYWH(0, 0, 2, 1); const SkRegion kTestRegion = testRegion(); const SkColor kTestColor = 0x01020304; const SkPaint kTestPaint; const SkPoint kTestPoints[3] = { {SkIntToScalar(0), SkIntToScalar(0)}, {SkIntToScalar(2), SkIntToScalar(1)}, {SkIntToScalar(0), SkIntToScalar(2)} }; const size_t kTestPointCount = 3; static SkBitmap testBitmap() { SkBitmap bitmap; createBitmap(&bitmap, 0x05060708); return bitmap; } SkBitmap kTestBitmap; // cannot be created during static init SkString kTestText("Hello World"); SkPoint kTestPoints2[] = { { SkIntToScalar(0), SkIntToScalar(1) }, { SkIntToScalar(1), SkIntToScalar(1) }, { SkIntToScalar(2), SkIntToScalar(1) }, { SkIntToScalar(3), SkIntToScalar(1) }, { SkIntToScalar(4), SkIntToScalar(1) }, { SkIntToScalar(5), SkIntToScalar(1) }, { SkIntToScalar(6), SkIntToScalar(1) }, { SkIntToScalar(7), SkIntToScalar(1) }, { SkIntToScalar(8), SkIntToScalar(1) }, { SkIntToScalar(9), SkIntToScalar(1) }, { SkIntToScalar(10), SkIntToScalar(1) }, }; /////////////////////////////////////////////////////////////////////////////// // Macros for defining test steps #define TEST_STEP(NAME, FUNCTION) \ class NAME##_TestStep : public CanvasTestStep{ \ public: \ virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \ FUNCTION (canvas, reporter, this); \ } \ virtual const char* name() const {return #NAME ;} \ }; \ static NAME##_TestStep NAME##_TestStepInstance; #define TEST_STEP_NO_PDF(NAME, FUNCTION) \ class NAME##_TestStep : public CanvasTestStep{ \ public: \ NAME##_TestStep() : CanvasTestStep(false) {} \ virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \ FUNCTION (canvas, reporter, this); \ } \ virtual const char* name() const {return #NAME ;} \ }; \ static NAME##_TestStep NAME##_TestStepInstance; #define SIMPLE_TEST_STEP(NAME, CALL) \ static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter*, \ CanvasTestStep*) { \ canvas-> CALL ; \ } \ TEST_STEP(NAME, NAME##TestStep ) #define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL) \ static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter* reporter, \ CanvasTestStep* testStep) { \ REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL , \ testStep->assertMessage()); \ } \ TEST_STEP(NAME, NAME##TestStep ) /////////////////////////////////////////////////////////////////////////////// // Basic test steps for most virtual methods in SkCanvas that draw or affect // the state of the canvas. SIMPLE_TEST_STEP(Translate, translate(SkIntToScalar(1), SkIntToScalar(2))); SIMPLE_TEST_STEP(Scale, scale(SkIntToScalar(1), SkIntToScalar(2))); SIMPLE_TEST_STEP(Rotate, rotate(SkIntToScalar(1))); SIMPLE_TEST_STEP(Skew, skew(SkIntToScalar(1), SkIntToScalar(2))); SIMPLE_TEST_STEP(Concat, concat(kTestMatrix)); SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix)); SIMPLE_TEST_STEP(ClipRect, clipRect(kTestRect)); SIMPLE_TEST_STEP(ClipPath, clipPath(kTestPath)); SIMPLE_TEST_STEP(ClipRegion, clipRegion(kTestRegion, SkRegion::kReplace_Op)); SIMPLE_TEST_STEP(Clear, clear(kTestColor)); SIMPLE_TEST_STEP(DrawPaint, drawPaint(kTestPaint)); SIMPLE_TEST_STEP(DrawPointsPoints, drawPoints(SkCanvas::kPoints_PointMode, kTestPointCount, kTestPoints, kTestPaint)); SIMPLE_TEST_STEP(DrawPointsLiness, drawPoints(SkCanvas::kLines_PointMode, kTestPointCount, kTestPoints, kTestPaint)); SIMPLE_TEST_STEP(DrawPointsPolygon, drawPoints(SkCanvas::kPolygon_PointMode, kTestPointCount, kTestPoints, kTestPaint)); SIMPLE_TEST_STEP(DrawRect, drawRect(kTestRect, kTestPaint)); SIMPLE_TEST_STEP(DrawPath, drawPath(kTestPath, kTestPaint)); SIMPLE_TEST_STEP(DrawBitmap, drawBitmap(kTestBitmap, 0, 0)); SIMPLE_TEST_STEP(DrawBitmapPaint, drawBitmap(kTestBitmap, 0, 0, &kTestPaint)); SIMPLE_TEST_STEP(DrawBitmapRect, drawBitmapRect(kTestBitmap, NULL, kTestRect, NULL)); SIMPLE_TEST_STEP(DrawBitmapRectSrcRect, drawBitmapRect(kTestBitmap, &kTestIRect, kTestRect, NULL)); SIMPLE_TEST_STEP(DrawBitmapRectPaint, drawBitmapRect(kTestBitmap, NULL, kTestRect, &kTestPaint)); SIMPLE_TEST_STEP(DrawBitmapMatrix, drawBitmapMatrix(kTestBitmap, kTestMatrix, NULL)); SIMPLE_TEST_STEP(DrawBitmapMatrixPaint, drawBitmapMatrix(kTestBitmap, kTestMatrix, &kTestPaint)); SIMPLE_TEST_STEP(DrawBitmapNine, drawBitmapNine(kTestBitmap, kTestIRect, kTestRect, NULL)); SIMPLE_TEST_STEP(DrawBitmapNinePaint, drawBitmapNine(kTestBitmap, kTestIRect, kTestRect, &kTestPaint)); SIMPLE_TEST_STEP(DrawSprite, drawSprite(kTestBitmap, 0, 0, NULL)); SIMPLE_TEST_STEP(DrawSpritePaint, drawSprite(kTestBitmap, 0, 0, &kTestPaint)); SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(), 0, 1, kTestPaint)); SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(), kTestText.size(), kTestPoints2, kTestPaint)); SIMPLE_TEST_STEP(DrawTextOnPath, drawTextOnPath(kTestText.c_str(), kTestText.size(), kTestPath, NULL, kTestPaint)); SIMPLE_TEST_STEP(DrawTextOnPathMatrix, drawTextOnPath(kTestText.c_str(), kTestText.size(), kTestPath, &kTestMatrix, kTestPaint)); SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size())); SIMPLE_TEST_STEP(BeginGroup, beginCommentGroup(kTestText.c_str())); SIMPLE_TEST_STEP(AddComment, addComment(kTestText.c_str(), kTestText.c_str())); SIMPLE_TEST_STEP(EndGroup, endCommentGroup()); /////////////////////////////////////////////////////////////////////////////// // Complex test steps // Save/restore calls cannot be in isolated simple test steps because the test // cases that use SkPicture require that save and restore calls be balanced. static void SaveMatrixStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->save(SkCanvas::kMatrix_SaveFlag); canvas->clipRegion(kTestRegion); canvas->translate(SkIntToScalar(1), SkIntToScalar(2)); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalMatrix().isIdentity(), testStep->assertMessage()); // REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() == kTestRegion, testStep->assertMessage()); } TEST_STEP(SaveMatrix, SaveMatrixStep); static void SaveClipStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->save(SkCanvas::kClip_SaveFlag); canvas->translate(SkIntToScalar(1), SkIntToScalar(2)); canvas->clipRegion(kTestRegion); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, !canvas->getTotalMatrix().isIdentity(), testStep->assertMessage()); // REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() != kTestRegion, testStep->assertMessage()); } TEST_STEP(SaveClip, SaveClipStep); static void SaveMatrixClipStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->save(SkCanvas::kMatrixClip_SaveFlag); canvas->translate(SkIntToScalar(1), SkIntToScalar(2)); canvas->clipRegion(kTestRegion); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalMatrix().isIdentity(), testStep->assertMessage()); // REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() != kTestRegion, testStep->assertMessage()); } TEST_STEP(SaveMatrixClip, SaveMatrixClipStep); static void SaveLayerStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->saveLayer(NULL, NULL); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(SaveLayer, SaveLayerStep); static void BoundedSaveLayerStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->saveLayer(&kTestRect, NULL); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep); static void PaintSaveLayerStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int saveCount = canvas->getSaveCount(); canvas->saveLayer(NULL, &kTestPaint); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount, testStep->assertMessage()); } TEST_STEP(PaintSaveLayer, PaintSaveLayerStep); static void TwoClipOpsStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { // This test exercises a functionality in SkPicture that leads to the // recording of restore offset placeholders. This test will trigger an // assertion at playback time if the placeholders are not properly // filled when the recording ends. canvas->clipRect(kTestRect); canvas->clipRegion(kTestRegion); } TEST_STEP(TwoClipOps, TwoClipOpsStep); // exercise fix for http://code.google.com/p/skia/issues/detail?id=560 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero') static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { SkPaint paint; paint.setStrokeWidth(SkIntToScalar(1)); paint.setStyle(SkPaint::kStroke_Style); canvas->drawPath(kNearlyZeroLengthPath, paint); } TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep); static void DrawVerticesShaderTestStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { SkPoint pts[4]; pts[0].set(0, 0); pts[1].set(SkIntToScalar(kWidth), 0); pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight)); pts[3].set(0, SkIntToScalar(kHeight)); SkPaint paint; SkShader* shader = SkShader::CreateBitmapShader(kTestBitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); paint.setShader(shader)->unref(); canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts, NULL, NULL, NULL, 0, paint); } // NYI: issue 240. TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep); static void DrawPictureTestStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { SkPictureRecorder recorder; SkCanvas* testCanvas = recorder.beginRecording(kWidth, kHeight, NULL, 0); testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1)); testCanvas->clipRect(kTestRect); testCanvas->drawRect(kTestRect, kTestPaint); SkAutoTUnref<SkPicture> testPicture(recorder.endRecording()); canvas->drawPicture(testPicture); } TEST_STEP(DrawPicture, DrawPictureTestStep); static void SaveRestoreTestStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { int baseSaveCount = canvas->getSaveCount(); int n = canvas->save(); REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount == n, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(), testStep->assertMessage()); canvas->save(); canvas->save(); REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 3 == canvas->getSaveCount(), testStep->assertMessage()); canvas->restoreToCount(baseSaveCount + 1); REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(), testStep->assertMessage()); // should this pin to 1, or be a no-op, or crash? canvas->restoreToCount(0); REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(), testStep->assertMessage()); } TEST_STEP(SaveRestore, SaveRestoreTestStep); static void DrawLayerTestStep(SkCanvas* canvas, skiatest::Reporter* reporter, CanvasTestStep* testStep) { REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->save(); REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->restore(); const SkRect* bounds = NULL; // null means include entire bounds const SkPaint* paint = NULL; canvas->saveLayer(bounds, paint); REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->saveLayer(bounds, paint); canvas->saveLayer(bounds, paint); REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->restore(); REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(), testStep->assertMessage()); canvas->restore(); // now layer count should be 0 REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(), testStep->assertMessage()); } TEST_STEP(DrawLayer, DrawLayerTestStep); static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { // This test step challenges the TestDeferredCanvasStateConsistency // test cases because the opaque paint can trigger an optimization // that discards previously recorded commands. The challenge is to maintain // correct clip and matrix stack state. canvas->resetMatrix(); canvas->rotate(SkIntToScalar(30)); canvas->save(); canvas->translate(SkIntToScalar(2), SkIntToScalar(1)); canvas->save(); canvas->scale(SkIntToScalar(3), SkIntToScalar(3)); SkPaint paint; paint.setColor(0xFFFFFFFF); canvas->drawPaint(paint); canvas->restore(); canvas->restore(); } TEST_STEP(NestedSaveRestoreWithSolidPaint, \ NestedSaveRestoreWithSolidPaintTestStep); static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, skiatest::Reporter*, CanvasTestStep*) { // This test step challenges the TestDeferredCanvasStateConsistency // test case because the canvas flush on a deferred canvas will // reset the recording session. The challenge is to maintain correct // clip and matrix stack state on the playback canvas. canvas->resetMatrix(); canvas->rotate(SkIntToScalar(30)); canvas->save(); canvas->translate(SkIntToScalar(2), SkIntToScalar(1)); canvas->save(); canvas->scale(SkIntToScalar(3), SkIntToScalar(3)); canvas->drawRect(kTestRect,kTestPaint); canvas->flush(); canvas->restore(); canvas->restore(); } TEST_STEP(NestedSaveRestoreWithFlush, \ NestedSaveRestoreWithFlushTestStep); static void AssertCanvasStatesEqual(skiatest::Reporter* reporter, const SkCanvas* canvas1, const SkCanvas* canvas2, CanvasTestStep* testStep) { REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() == canvas2->getDeviceSize(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas1->getSaveCount() == canvas2->getSaveCount(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() == canvas2->isDrawingToLayer(), testStep->assertMessage()); SkRect bounds1, bounds2; REPORTER_ASSERT_MESSAGE(reporter, canvas1->getClipBounds(&bounds1) == canvas2->getClipBounds(&bounds2), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() == canvas2->getDrawFilter(), testStep->assertMessage()); SkIRect deviceBounds1, deviceBounds2; REPORTER_ASSERT_MESSAGE(reporter, canvas1->getClipDeviceBounds(&deviceBounds1) == canvas2->getClipDeviceBounds(&deviceBounds2), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, deviceBounds1 == deviceBounds2, testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalMatrix() == canvas2->getTotalMatrix(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, equal_clips(*canvas1, *canvas2), testStep->assertMessage()); // The following test code is commented out because the test fails when // the canvas is an SkPictureRecord or SkDeferredCanvas // Issue: http://code.google.com/p/skia/issues/detail?id=498 // Also, creating a LayerIter on an SkProxyCanvas crashes // Issue: http://code.google.com/p/skia/issues/detail?id=499 /* SkCanvas::LayerIter layerIter1(const_cast<SkCanvas*>(canvas1), false); SkCanvas::LayerIter layerIter2(const_cast<SkCanvas*>(canvas2), false); while (!layerIter1.done() && !layerIter2.done()) { REPORTER_ASSERT_MESSAGE(reporter, layerIter1.matrix() == layerIter2.matrix(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, layerIter1.clip() == layerIter2.clip(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, layerIter1.paint() == layerIter2.paint(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, layerIter1.x() == layerIter2.x(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, layerIter1.y() == layerIter2.y(), testStep->assertMessage()); layerIter1.next(); layerIter2.next(); } REPORTER_ASSERT_MESSAGE(reporter, layerIter1.done(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, layerIter2.done(), testStep->assertMessage()); */ } // The following class groups static functions that need to access // the privates members of SkPictureRecord class SkPictureTester { private: static int EQ(const SkFlatData* a, const SkFlatData* b) { return *a == *b; } static void AssertFlattenedObjectsEqual( SkPictureRecord* referenceRecord, SkPictureRecord* testRecord, skiatest::Reporter* reporter, CanvasTestStep* testStep) { REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fBitmapHeap->count() == testRecord->fBitmapHeap->count(), testStep->assertMessage()); REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPaints.count() == testRecord->fPaints.count(), testStep->assertMessage()); for (int i = 0; i < referenceRecord->fPaints.count(); ++i) { REPORTER_ASSERT_MESSAGE(reporter, EQ(referenceRecord->fPaints[i], testRecord->fPaints[i]), testStep->assertMessage()); } REPORTER_ASSERT_MESSAGE(reporter, !referenceRecord->fPathHeap == !testRecord->fPathHeap, testStep->assertMessage()); // The following tests are commented out because they currently // fail. Issue: http://code.google.com/p/skia/issues/detail?id=507 /* if (referenceRecord->fPathHeap) { REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPathHeap->count() == testRecord->fPathHeap->count(), testStep->assertMessage()); for (int i = 0; i < referenceRecord->fPathHeap->count(); ++i) { REPORTER_ASSERT_MESSAGE(reporter, (*referenceRecord->fPathHeap)[i] == (*testRecord->fPathHeap)[i], testStep->assertMessage()); } } */ } public: static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter, CanvasTestStep* testStep, uint32_t recordFlags) { // Verify that when a test step is executed twice, no extra resources // are flattened during the second execution testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat); SkPictureRecorder referenceRecorder; SkCanvas* referenceCanvas = referenceRecorder.beginRecording(kWidth, kHeight, NULL, recordFlags); testStep->draw(referenceCanvas, reporter); SkPictureRecorder testRecorder; SkCanvas* testCanvas = testRecorder.beginRecording(kWidth, kHeight, NULL, recordFlags); testStep->draw(testCanvas, reporter); testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat); testStep->draw(testCanvas, reporter); SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(referenceCanvas); SkPictureRecord* testRecord = static_cast<SkPictureRecord*>(testCanvas); testStep->setAssertMessageFormat(kPictureResourceReuseMessageFormat); AssertFlattenedObjectsEqual(referenceRecord, testRecord, reporter, testStep); } }; static void TestPdfDevice(skiatest::Reporter* reporter, CanvasTestStep* testStep) { SkISize pageSize = SkISize::Make(kWidth, kHeight); SkPDFDevice device(pageSize, pageSize, SkMatrix::I()); SkCanvas canvas(&device); testStep->setAssertMessageFormat(kPdfAssertMessageFormat); testStep->draw(&canvas, reporter); SkPDFDocument doc; doc.appendPage(&device); SkDynamicMemoryWStream stream; doc.emitPDF(&stream); } // The following class groups static functions that need to access // the privates members of SkDeferredCanvas class SkDeferredCanvasTester { public: static void TestDeferredCanvasStateConsistency( skiatest::Reporter* reporter, CanvasTestStep* testStep, const SkCanvas& referenceCanvas, bool silent) { SkAutoTUnref<SkSurface> surface(createSurface(0xFFFFFFFF)); SkAutoTUnref<SkDeferredCanvas> deferredCanvas(SkDeferredCanvas::Create(surface.get())); testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat); testStep->draw(deferredCanvas, reporter); testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat); AssertCanvasStatesEqual(reporter, deferredCanvas, &referenceCanvas, testStep); if (silent) { deferredCanvas->silentFlush(); } else { deferredCanvas->flush(); } testStep->setAssertMessageFormat( silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat : kDeferredPostFlushPlaybackAssertMessageFormat); AssertCanvasStatesEqual(reporter, deferredCanvas->immediateCanvas(), &referenceCanvas, testStep); // Verified that deferred canvas state is not affected by flushing // pending draw operations // The following test code is commented out because it currently fails. // Issue: http://code.google.com/p/skia/issues/detail?id=496 /* testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat); AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas, testStep); */ } }; // unused static void TestProxyCanvasStateConsistency( skiatest::Reporter* reporter, CanvasTestStep* testStep, const SkCanvas& referenceCanvas) { SkBitmap indirectStore; createBitmap(&indirectStore, 0xFFFFFFFF); SkBitmapDevice indirectDevice(indirectStore); SkCanvas indirectCanvas(&indirectDevice); SkProxyCanvas proxyCanvas(&indirectCanvas); testStep->setAssertMessageFormat(kProxyDrawAssertMessageFormat); testStep->draw(&proxyCanvas, reporter); // Verify that the SkProxyCanvas reports consitent state testStep->setAssertMessageFormat(kProxyStateAssertMessageFormat); AssertCanvasStatesEqual(reporter, &proxyCanvas, &referenceCanvas, testStep); // Verify that the indirect canvas reports consitent state testStep->setAssertMessageFormat(kProxyIndirectStateAssertMessageFormat); AssertCanvasStatesEqual(reporter, &indirectCanvas, &referenceCanvas, testStep); } // unused static void TestNWayCanvasStateConsistency( skiatest::Reporter* reporter, CanvasTestStep* testStep, const SkCanvas& referenceCanvas) { SkBitmap indirectStore1; createBitmap(&indirectStore1, 0xFFFFFFFF); SkBitmapDevice indirectDevice1(indirectStore1); SkCanvas indirectCanvas1(&indirectDevice1); SkBitmap indirectStore2; createBitmap(&indirectStore2, 0xFFFFFFFF); SkBitmapDevice indirectDevice2(indirectStore2); SkCanvas indirectCanvas2(&indirectDevice2); SkISize canvasSize = referenceCanvas.getDeviceSize(); SkNWayCanvas nWayCanvas(canvasSize.width(), canvasSize.height()); nWayCanvas.addCanvas(&indirectCanvas1); nWayCanvas.addCanvas(&indirectCanvas2); testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat); testStep->draw(&nWayCanvas, reporter); // Verify that the SkProxyCanvas reports consitent state testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat); AssertCanvasStatesEqual(reporter, &nWayCanvas, &referenceCanvas, testStep); // Verify that the indirect canvases report consitent state testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat); AssertCanvasStatesEqual(reporter, &indirectCanvas1, &referenceCanvas, testStep); testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat); AssertCanvasStatesEqual(reporter, &indirectCanvas2, &referenceCanvas, testStep); } /* * This sub-test verifies that the test step passes when executed * with SkCanvas and with classes derrived from SkCanvas. It also verifies * that the all canvas derivatives report the same state as an SkCanvas * after having executed the test step. */ static void TestOverrideStateConsistency(skiatest::Reporter* reporter, CanvasTestStep* testStep) { SkBitmap referenceStore; createBitmap(&referenceStore, 0xFFFFFFFF); SkBitmapDevice referenceDevice(referenceStore); SkCanvas referenceCanvas(&referenceDevice); testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat); testStep->draw(&referenceCanvas, reporter); SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, false); SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, true); // The following test code is disabled because SkProxyCanvas is // missing a lot of virtual overrides on get* methods, which are used // to verify canvas state. // Issue: http://code.google.com/p/skia/issues/detail?id=500 if (false) { // avoid bit rot, suppress warning TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas); } // The following test code is disabled because SkNWayCanvas does not // report correct clipping and device bounds information // Issue: http://code.google.com/p/skia/issues/detail?id=501 if (false) { // avoid bit rot, suppress warning TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas); } if (false) { // avoid bit rot, suppress warning test_clipVisitor(reporter, &referenceCanvas); } } static void test_newraster(skiatest::Reporter* reporter) { SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10); SkCanvas* canvas = SkCanvas::NewRaster(info); REPORTER_ASSERT(reporter, canvas); SkImageInfo info2; size_t rowBytes; const SkPMColor* addr = (const SkPMColor*)canvas->peekPixels(&info2, &rowBytes); REPORTER_ASSERT(reporter, addr); REPORTER_ASSERT(reporter, info == info2); for (int y = 0; y < info.height(); ++y) { for (int x = 0; x < info.width(); ++x) { REPORTER_ASSERT(reporter, 0 == addr[x]); } addr = (const SkPMColor*)((const char*)addr + rowBytes); } SkDELETE(canvas); // now try a deliberately bad info info.fWidth = -1; REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); // too big info.fWidth = 1 << 30; info.fHeight = 1 << 30; REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); // not a valid pixel type info.fWidth = info.fHeight = 10; info.fColorType = kUnknown_SkColorType; REPORTER_ASSERT(reporter, NULL == SkCanvas::NewRaster(info)); // We should succeed with a zero-sized valid info info = SkImageInfo::MakeN32Premul(0, 0); canvas = SkCanvas::NewRaster(info); REPORTER_ASSERT(reporter, canvas); SkDELETE(canvas); } DEF_TEST(Canvas, reporter) { // Init global here because bitmap pixels cannot be alocated during // static initialization kTestBitmap = testBitmap(); for (int testStep = 0; testStep < testStepArray().count(); testStep++) { TestOverrideStateConsistency(reporter, testStepArray()[testStep]); SkPictureTester::TestPictureFlattenedObjectReuse(reporter, testStepArray()[testStep], 0); if (testStepArray()[testStep]->enablePdfTesting()) { TestPdfDevice(reporter, testStepArray()[testStep]); } } // Explicitly call reset(), so we don't leak the pixels (since kTestBitmap is a global) kTestBitmap.reset(); test_newraster(reporter); }