1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkArenaAlloc.h" 9 #include "SkBitmap.h" 10 #include "SkCanvas.h" 11 #include "SkColorSpaceXformer.h" 12 #include "SkDrawLooper.h" 13 #include "SkLightingImageFilter.h" 14 #include "SkPoint3.h" 15 #include "SkTypes.h" 16 #include "Test.h" 17 18 /* 19 * Subclass of looper that just draws once, with an offset in X. 20 */ 21 class TestLooper : public SkDrawLooper { 22 public: 23 24 SkDrawLooper::Context* makeContext(SkCanvas*, SkArenaAlloc* alloc) const override { 25 return alloc->make<TestDrawLooperContext>(); 26 } 27 28 sk_sp<SkDrawLooper> onMakeColorSpace(SkColorSpaceXformer*) const override { 29 return nullptr; 30 } 31 32 #ifndef SK_IGNORE_TO_STRING 33 void toString(SkString* str) const override { 34 str->append("TestLooper:"); 35 } 36 #endif 37 38 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TestLooper) 39 40 private: 41 class TestDrawLooperContext : public SkDrawLooper::Context { 42 public: 43 TestDrawLooperContext() : fOnce(true) {} 44 ~TestDrawLooperContext() override {} 45 46 bool next(SkCanvas* canvas, SkPaint*) override { 47 if (fOnce) { 48 fOnce = false; 49 canvas->translate(SkIntToScalar(10), 0); 50 return true; 51 } 52 return false; 53 } 54 55 private: 56 bool fOnce; 57 }; 58 }; 59 60 sk_sp<SkFlattenable> TestLooper::CreateProc(SkReadBuffer&) { return sk_make_sp<TestLooper>(); } 61 62 static void test_drawBitmap(skiatest::Reporter* reporter) { 63 SkBitmap src; 64 src.allocN32Pixels(10, 10); 65 src.eraseColor(SK_ColorWHITE); 66 67 SkBitmap dst; 68 dst.allocN32Pixels(10, 10); 69 dst.eraseColor(SK_ColorTRANSPARENT); 70 71 SkCanvas canvas(dst); 72 SkPaint paint; 73 74 // we are initially transparent 75 REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); 76 77 // we see the bitmap drawn 78 canvas.drawBitmap(src, 0, 0, &paint); 79 REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5)); 80 81 // reverify we are clear again 82 dst.eraseColor(SK_ColorTRANSPARENT); 83 REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); 84 85 // if the bitmap is clipped out, we don't draw it 86 canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint); 87 REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5)); 88 89 // now install our looper, which will draw, since it internally translates 90 // to the left. The test is to ensure that canvas' quickReject machinary 91 // allows us through, even though sans-looper we would look like we should 92 // be clipped out. 93 paint.setLooper(sk_make_sp<TestLooper>()); 94 canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint); 95 REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5)); 96 } 97 98 static void test_layers(skiatest::Reporter* reporter) { 99 SkCanvas canvas(100, 100); 100 101 SkRect r = SkRect::MakeWH(10, 10); 102 REPORTER_ASSERT(reporter, false == canvas.quickReject(r)); 103 104 r.offset(300, 300); 105 REPORTER_ASSERT(reporter, true == canvas.quickReject(r)); 106 107 // Test that saveLayer updates quickReject 108 SkRect bounds = SkRect::MakeLTRB(50, 50, 70, 70); 109 canvas.saveLayer(&bounds, nullptr); 110 REPORTER_ASSERT(reporter, true == canvas.quickReject(SkRect::MakeWH(10, 10))); 111 REPORTER_ASSERT(reporter, false == canvas.quickReject(SkRect::MakeWH(60, 60))); 112 } 113 114 static void test_quick_reject(skiatest::Reporter* reporter) { 115 SkCanvas canvas(100, 100); 116 SkRect r0 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f); 117 SkRect r1 = SkRect::MakeLTRB(-50.0f, 110.0f, 50.0f, 120.0f); 118 SkRect r2 = SkRect::MakeLTRB(110.0f, -50.0f, 120.0f, 50.0f); 119 SkRect r3 = SkRect::MakeLTRB(-120.0f, -50.0f, 120.0f, 50.0f); 120 SkRect r4 = SkRect::MakeLTRB(-50.0f, -120.0f, 50.0f, 120.0f); 121 SkRect r5 = SkRect::MakeLTRB(-120.0f, -120.0f, 120.0f, 120.0f); 122 SkRect r6 = SkRect::MakeLTRB(-120.0f, -120.0f, -110.0f, -110.0f); 123 SkRect r7 = SkRect::MakeLTRB(SK_ScalarNaN, -50.0f, 50.0f, 50.0f); 124 SkRect r8 = SkRect::MakeLTRB(-50.0f, SK_ScalarNaN, 50.0f, 50.0f); 125 SkRect r9 = SkRect::MakeLTRB(-50.0f, -50.0f, SK_ScalarNaN, 50.0f); 126 SkRect r10 = SkRect::MakeLTRB(-50.0f, -50.0f, 50.0f, SK_ScalarNaN); 127 REPORTER_ASSERT(reporter, false == canvas.quickReject(r0)); 128 REPORTER_ASSERT(reporter, true == canvas.quickReject(r1)); 129 REPORTER_ASSERT(reporter, true == canvas.quickReject(r2)); 130 REPORTER_ASSERT(reporter, false == canvas.quickReject(r3)); 131 REPORTER_ASSERT(reporter, false == canvas.quickReject(r4)); 132 REPORTER_ASSERT(reporter, false == canvas.quickReject(r5)); 133 REPORTER_ASSERT(reporter, true == canvas.quickReject(r6)); 134 REPORTER_ASSERT(reporter, true == canvas.quickReject(r7)); 135 REPORTER_ASSERT(reporter, true == canvas.quickReject(r8)); 136 REPORTER_ASSERT(reporter, true == canvas.quickReject(r9)); 137 REPORTER_ASSERT(reporter, true == canvas.quickReject(r10)); 138 139 SkMatrix m = SkMatrix::MakeScale(2.0f); 140 m.setTranslateX(10.0f); 141 m.setTranslateY(10.0f); 142 canvas.setMatrix(m); 143 SkRect r11 = SkRect::MakeLTRB(5.0f, 5.0f, 100.0f, 100.0f); 144 SkRect r12 = SkRect::MakeLTRB(5.0f, 50.0f, 100.0f, 100.0f); 145 SkRect r13 = SkRect::MakeLTRB(50.0f, 5.0f, 100.0f, 100.0f); 146 REPORTER_ASSERT(reporter, false == canvas.quickReject(r11)); 147 REPORTER_ASSERT(reporter, true == canvas.quickReject(r12)); 148 REPORTER_ASSERT(reporter, true == canvas.quickReject(r13)); 149 } 150 151 DEF_TEST(QuickReject, reporter) { 152 test_drawBitmap(reporter); 153 test_layers(reporter); 154 test_quick_reject(reporter); 155 } 156 157 // Regression test to make sure that we keep fIsScaleTranslate up to date on the canvas. 158 // It is possible to set a new matrix on the canvas without calling setMatrix(). This tests 159 // that code path. 160 DEF_TEST(QuickReject_MatrixState, reporter) { 161 SkCanvas canvas(100, 100); 162 163 SkMatrix matrix; 164 matrix.setRotate(45.0f); 165 canvas.setMatrix(matrix); 166 167 SkPaint paint; 168 sk_sp<SkImageFilter> filter = SkLightingImageFilter::MakeDistantLitDiffuse( 169 SkPoint3::Make(1.0f, 1.0f, 1.0f), 0xFF0000FF, 2.0f, 0.5f, nullptr); 170 REPORTER_ASSERT(reporter, filter); 171 paint.setImageFilter(filter); 172 SkCanvas::SaveLayerRec rec; 173 rec.fPaint = &paint; 174 canvas.saveLayer(rec); 175 176 // quickReject() will assert if the matrix is out of sync. 177 canvas.quickReject(SkRect::MakeWH(100.0f, 100.0f)); 178 } 179