• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 /*  Description:
9  *      This test defines a series of elementatry test steps that perform
10  *      a single or a small group of canvas API calls. Each test step is
11  *      used in several test cases that verify that different types of SkCanvas
12  *      flavors and derivatives pass it and yield consistent behavior. The
13  *      test cases analyse results that are queryable through the API. They do
14  *      not look at rendering results.
15  *
16  *  Adding test stepss:
17  *      The general pattern for creating a new test step is to write a test
18  *      function of the form:
19  *
20  *          static void MyTestStepFunction(SkCanvas* canvas,
21  *                                         const TestData& d,
22  *                                         skiatest::Reporter* reporter,
23  *                                         CanvasTestStep* testStep)
24  *          {
25  *              canvas->someCanvasAPImethod();
26  *              (...)
27  *              REPORTER_ASSERT_MESSAGE(reporter, (...), \
28  *                  testStep->assertMessage());
29  *          }
30  *
31  *      The definition of the test step function should be followed by an
32  *      invocation of the TEST_STEP macro, which generates a class and
33  *      instance for the test step:
34  *
35  *          TEST_STEP(MyTestStep, MyTestStepFunction)
36  *
37  *      There are also short hand macros for defining simple test steps
38  *      in a single line of code.  A simple test step is a one that is made
39  *      of a single canvas API call.
40  *
41  *          SIMPLE_TEST_STEP(MytestStep, someCanvasAPIMethod());
42  *
43  *      There is another macro called SIMPLE_TEST_STEP_WITH_ASSERT that
44  *      works the same way as SIMPLE_TEST_STEP, and additionally verifies
45  *      that the invoked method returns a non-zero value.
46  */
47 
48 #include "SkBitmap.h"
49 #include "SkCanvas.h"
50 #include "SkClipStack.h"
51 #include "SkDocument.h"
52 #include "SkMatrix.h"
53 #include "SkNWayCanvas.h"
54 #include "SkPaint.h"
55 #include "SkPaintFilterCanvas.h"
56 #include "SkPath.h"
57 #include "SkPicture.h"
58 #include "SkPictureRecord.h"
59 #include "SkPictureRecorder.h"
60 #include "SkRasterClip.h"
61 #include "SkRect.h"
62 #include "SkRegion.h"
63 #include "SkShader.h"
64 #include "SkStream.h"
65 #include "SkSurface.h"
66 #include "SkTemplates.h"
67 #include "SkTDArray.h"
68 #include "Test.h"
69 
DEF_TEST(canvas_clipbounds,reporter)70 DEF_TEST(canvas_clipbounds, reporter) {
71     SkCanvas canvas(10, 10);
72     SkIRect irect, irect2;
73     SkRect rect, rect2;
74 
75     irect = canvas.getDeviceClipBounds();
76     REPORTER_ASSERT(reporter, irect == SkIRect::MakeWH(10, 10));
77     REPORTER_ASSERT(reporter, canvas.getDeviceClipBounds(&irect2));
78     REPORTER_ASSERT(reporter, irect == irect2);
79 
80     // local bounds are always too big today -- can we trim them?
81     rect = canvas.getLocalClipBounds();
82     REPORTER_ASSERT(reporter, rect.contains(SkRect::MakeWH(10, 10)));
83     REPORTER_ASSERT(reporter, canvas.getLocalClipBounds(&rect2));
84     REPORTER_ASSERT(reporter, rect == rect2);
85 
86     canvas.clipRect(SkRect::MakeEmpty());
87 
88     irect = canvas.getDeviceClipBounds();
89     REPORTER_ASSERT(reporter, irect == SkIRect::MakeEmpty());
90     REPORTER_ASSERT(reporter, !canvas.getDeviceClipBounds(&irect2));
91     REPORTER_ASSERT(reporter, irect == irect2);
92 
93     rect = canvas.getLocalClipBounds();
94     REPORTER_ASSERT(reporter, rect == SkRect::MakeEmpty());
95     REPORTER_ASSERT(reporter, !canvas.getLocalClipBounds(&rect2));
96     REPORTER_ASSERT(reporter, rect == rect2);
97 
98     // Test for wacky sizes that we (historically) have guarded against
99     {
100         SkCanvas c(-10, -20);
101         REPORTER_ASSERT(reporter, c.getBaseLayerSize() == SkISize::MakeEmpty());
102 
103         SkPictureRecorder().beginRecording({ 5, 5, 4, 4 });
104     }
105 }
106 
107 // Will call proc with multiple styles of canse (recording, raster, pdf)
108 //
multi_canvas_driver(int w,int h,F proc)109 template <typename F> static void multi_canvas_driver(int w, int h, F proc) {
110     proc(SkPictureRecorder().beginRecording(SkRect::MakeIWH(w, h)));
111 
112     SkNullWStream stream;
113     proc(SkDocument::MakePDF(&stream)->beginPage(SkIntToScalar(w), SkIntToScalar(h)));
114 
115     proc(SkSurface::MakeRasterN32Premul(w, h, nullptr)->getCanvas());
116 }
117 
118 
119 const SkIRect gBaseRestrictedR = { 0, 0, 10, 10 };
120 
test_restriction(skiatest::Reporter * reporter,SkCanvas * canvas)121 static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
122     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);
123 
124     const SkIRect restrictionR = { 2, 2, 8, 8 };
125     canvas->androidFramework_setDeviceClipRestriction(restrictionR);
126     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);
127 
128     const SkIRect clipR = { 4, 4, 6, 6 };
129     canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
130     REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);
131 
132     // now test that expanding clipops can't exceed the restriction
133     const SkClipOp expanders[] = {
134         SkClipOp::kUnion_deprecated,
135         SkClipOp::kXOR_deprecated,
136         SkClipOp::kReverseDifference_deprecated,
137         SkClipOp::kReplace_deprecated,
138     };
139 
140     const SkRect expandR = { 0, 0, 5, 9 };
141     SkASSERT(!SkRect::Make(restrictionR).contains(expandR));
142 
143     for (SkClipOp op : expanders) {
144         canvas->save();
145         canvas->clipRect(expandR, op);
146         REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds()));
147         canvas->restore();
148     }
149 }
150 
151 /**
152  *  Clip restriction logic exists in the canvas itself, and in various kinds of devices.
153  *
154  *  This test explicitly tries to exercise that variety:
155  *  - picture : empty device but exercises canvas itself
156  *  - pdf : uses SkClipStack in its device (as does SVG and GPU)
157  *  - raster : uses SkRasterClip in its device
158  */
DEF_TEST(canvas_clip_restriction,reporter)159 DEF_TEST(canvas_clip_restriction, reporter) {
160     multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
161                         [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
162 }
163 
DEF_TEST(canvas_empty_clip,reporter)164 DEF_TEST(canvas_empty_clip, reporter) {
165     multi_canvas_driver(50, 50, [reporter](SkCanvas* canvas) {
166         canvas->save();
167         canvas->clipRect({0, 0, 20, 40 });
168         REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
169         canvas->clipRect({30, 0, 50, 40 });
170         REPORTER_ASSERT(reporter, canvas->isClipEmpty());
171     });
172 }
173 
174 static const int kWidth = 2, kHeight = 2;
175 
createBitmap(SkBitmap * bm,SkColor color)176 static void createBitmap(SkBitmap* bm, SkColor color) {
177     bm->allocN32Pixels(kWidth, kHeight);
178     bm->eraseColor(color);
179 }
180 
181 ///////////////////////////////////////////////////////////////////////////////
182 // Constants used by test steps
183 const SkPoint kTestPoints[] = {
184     {SkIntToScalar(0), SkIntToScalar(0)},
185     {SkIntToScalar(2), SkIntToScalar(1)},
186     {SkIntToScalar(0), SkIntToScalar(2)}
187 };
188 const SkPoint kTestPoints2[] = {
189     { SkIntToScalar(0), SkIntToScalar(1) },
190     { SkIntToScalar(1), SkIntToScalar(1) },
191     { SkIntToScalar(2), SkIntToScalar(1) },
192     { SkIntToScalar(3), SkIntToScalar(1) },
193     { SkIntToScalar(4), SkIntToScalar(1) },
194     { SkIntToScalar(5), SkIntToScalar(1) },
195     { SkIntToScalar(6), SkIntToScalar(1) },
196     { SkIntToScalar(7), SkIntToScalar(1) },
197     { SkIntToScalar(8), SkIntToScalar(1) },
198     { SkIntToScalar(9), SkIntToScalar(1) },
199     { SkIntToScalar(10), SkIntToScalar(1) }
200 };
201 
202 struct TestData {
203 public:
TestDataTestData204     TestData()
205     : fRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
206                                  SkIntToScalar(2), SkIntToScalar(1)))
207     , fMatrix(TestMatrix())
208     , fPath(TestPath())
209     , fNearlyZeroLengthPath(TestNearlyZeroLengthPath())
210     , fIRect(SkIRect::MakeXYWH(0, 0, 2, 1))
211     , fRegion(TestRegion())
212     , fColor(0x01020304)
213     , fPoints(kTestPoints)
214     , fPointCount(3)
215     , fWidth(2)
216     , fHeight(2)
217     , fText("Hello World")
218     , fPoints2(kTestPoints2)
219     , fBitmap(TestBitmap())
220     { }
221 
222     SkRect fRect;
223     SkMatrix fMatrix;
224     SkPath fPath;
225     SkPath fNearlyZeroLengthPath;
226     SkIRect fIRect;
227     SkRegion fRegion;
228     SkColor fColor;
229     SkPaint fPaint;
230     const SkPoint* fPoints;
231     size_t fPointCount;
232     int fWidth;
233     int fHeight;
234     SkString fText;
235     const SkPoint* fPoints2;
236     SkBitmap fBitmap;
237 
238 private:
TestMatrixTestData239     static SkMatrix TestMatrix() {
240         SkMatrix matrix;
241         matrix.reset();
242         matrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
243 
244         return matrix;
245     }
TestPathTestData246     static SkPath TestPath() {
247         SkPath path;
248         path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
249                                       SkIntToScalar(2), SkIntToScalar(1)));
250         return path;
251     }
TestNearlyZeroLengthPathTestData252     static SkPath TestNearlyZeroLengthPath() {
253         SkPath path;
254         SkPoint pt1 = { 0, 0 };
255         SkPoint pt2 = { 0, SK_ScalarNearlyZero };
256         SkPoint pt3 = { SkIntToScalar(1), 0 };
257         SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 };
258         path.moveTo(pt1);
259         path.lineTo(pt2);
260         path.lineTo(pt3);
261         path.lineTo(pt4);
262         return path;
263     }
TestRegionTestData264     static SkRegion TestRegion() {
265         SkRegion region;
266         SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1);
267         region.setRect(rect);
268         return region;
269     }
TestBitmapTestData270     static SkBitmap TestBitmap() {
271         SkBitmap bitmap;
272         createBitmap(&bitmap, 0x05060708);
273         return bitmap;
274     }
275 };
276 
277 class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor {
278 public:
Canvas2CanvasClipVisitor(SkCanvas * target)279     Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {}
280 
clipRect(const SkRect & r,SkClipOp op,bool aa)281     void clipRect(const SkRect& r, SkClipOp op, bool aa) override {
282         fTarget->clipRect(r, op, aa);
283     }
clipRRect(const SkRRect & r,SkClipOp op,bool aa)284     void clipRRect(const SkRRect& r, SkClipOp op, bool aa) override {
285         fTarget->clipRRect(r, op, aa);
286     }
clipPath(const SkPath & p,SkClipOp op,bool aa)287     void clipPath(const SkPath& p, SkClipOp op, bool aa) override {
288         fTarget->clipPath(p, op, aa);
289     }
290 
291 private:
292     SkCanvas* fTarget;
293 };
294 
295 // Format strings that describe the test context.  The %s token is where
296 // the name of the test step is inserted.  The context is required for
297 // disambiguating the error in the case of failures that are reported in
298 // functions that are called multiple times in different contexts (test
299 // cases and test steps).
300 static const char* const kDefaultAssertMessageFormat = "%s";
301 static const char* const kCanvasDrawAssertMessageFormat =
302     "Drawing test step %s with SkCanvas";
303 static const char* const kPdfAssertMessageFormat =
304     "PDF sanity check failed %s";
305 
306 class CanvasTestStep;
testStepArray()307 static SkTDArray<CanvasTestStep*>& testStepArray() {
308     static SkTDArray<CanvasTestStep*> theTests;
309     return theTests;
310 }
311 
312 class CanvasTestStep {
313 public:
CanvasTestStep(bool fEnablePdfTesting=true)314     CanvasTestStep(bool fEnablePdfTesting = true) {
315         *testStepArray().append() = this;
316         fAssertMessageFormat = kDefaultAssertMessageFormat;
317         this->fEnablePdfTesting = fEnablePdfTesting;
318     }
~CanvasTestStep()319     virtual ~CanvasTestStep() { }
320 
321     virtual void draw(SkCanvas*, const TestData&, skiatest::Reporter*) = 0;
322     virtual const char* name() const = 0;
323 
assertMessage()324     const char* assertMessage() {
325         fAssertMessage.printf(fAssertMessageFormat, name());
326         return fAssertMessage.c_str();
327     }
328 
setAssertMessageFormat(const char * format)329     void setAssertMessageFormat(const char* format) {
330         fAssertMessageFormat = format;
331     }
332 
enablePdfTesting()333     bool enablePdfTesting() { return fEnablePdfTesting; }
334 
335 private:
336     SkString fAssertMessage;
337     const char* fAssertMessageFormat;
338     bool fEnablePdfTesting;
339 };
340 
341 ///////////////////////////////////////////////////////////////////////////////
342 // Macros for defining test steps
343 
344 #define TEST_STEP(NAME, FUNCTION)                                       \
345 class NAME##_TestStep : public CanvasTestStep{                          \
346 public:                                                                 \
347     virtual void draw(SkCanvas* canvas, const TestData& d,       \
348         skiatest::Reporter* reporter) {                                 \
349         FUNCTION (canvas, d, reporter, this);                    \
350     }                                                                   \
351     virtual const char* name() const {return #NAME ;}                   \
352 };                                                                      \
353 static NAME##_TestStep NAME##_TestStepInstance;
354 
355 #define TEST_STEP_NO_PDF(NAME, FUNCTION)                                \
356 class NAME##_TestStep : public CanvasTestStep{                          \
357 public:                                                                 \
358     NAME##_TestStep() : CanvasTestStep(false) {}                        \
359     virtual void draw(SkCanvas* canvas, const TestData& d,       \
360         skiatest::Reporter* reporter) {                                 \
361         FUNCTION (canvas, d, reporter, this);                    \
362     }                                                                   \
363     virtual const char* name() const {return #NAME ;}                   \
364 };                                                                      \
365 static NAME##_TestStep NAME##_TestStepInstance;
366 
367 #define SIMPLE_TEST_STEP(NAME, CALL)                                    \
368 static void NAME##TestStep(SkCanvas* canvas, const TestData& d,  \
369     skiatest::Reporter*, CanvasTestStep*) {                             \
370     canvas-> CALL ;                                                     \
371 }                                                                       \
372 TEST_STEP(NAME, NAME##TestStep )
373 
374 #define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL)                           \
375 static void NAME##TestStep(SkCanvas* canvas, const TestData& d,     \
376     skiatest::Reporter*, CanvasTestStep* testStep) {                       \
377     REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL ,                      \
378         testStep->assertMessage());                                        \
379 }                                                                          \
380 TEST_STEP(NAME, NAME##TestStep )
381 
382 
383 ///////////////////////////////////////////////////////////////////////////////
384 // Basic test steps for most virtual methods in SkCanvas that draw or affect
385 // the state of the canvas.
386 
387 SIMPLE_TEST_STEP(Translate, translate(SkIntToScalar(1), SkIntToScalar(2)));
388 SIMPLE_TEST_STEP(Scale, scale(SkIntToScalar(1), SkIntToScalar(2)));
389 SIMPLE_TEST_STEP(Rotate, rotate(SkIntToScalar(1)));
390 SIMPLE_TEST_STEP(Skew, skew(SkIntToScalar(1), SkIntToScalar(2)));
391 SIMPLE_TEST_STEP(Concat, concat(d.fMatrix));
392 SIMPLE_TEST_STEP(SetMatrix, setMatrix(d.fMatrix));
393 SIMPLE_TEST_STEP(ClipRect, clipRect(d.fRect));
394 SIMPLE_TEST_STEP(ClipPath, clipPath(d.fPath));
395 SIMPLE_TEST_STEP(ClipRegion, clipRegion(d.fRegion, kReplace_SkClipOp));
396 SIMPLE_TEST_STEP(Clear, clear(d.fColor));
397 
398 ///////////////////////////////////////////////////////////////////////////////
399 // Complex test steps
400 
SaveMatrixClipStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)401 static void SaveMatrixClipStep(SkCanvas* canvas, const TestData& d,
402                                skiatest::Reporter* reporter, CanvasTestStep* testStep) {
403     int saveCount = canvas->getSaveCount();
404     canvas->save();
405     canvas->translate(SkIntToScalar(1), SkIntToScalar(2));
406     canvas->clipRegion(d.fRegion);
407     canvas->restore();
408     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
409         testStep->assertMessage());
410     REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalMatrix().isIdentity(),
411         testStep->assertMessage());
412 //    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() != kTestRegion, testStep->assertMessage());
413 }
414 TEST_STEP(SaveMatrixClip, SaveMatrixClipStep);
415 
SaveLayerStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)416 static void SaveLayerStep(SkCanvas* canvas, const TestData& d,
417                           skiatest::Reporter* reporter, CanvasTestStep* testStep) {
418     int saveCount = canvas->getSaveCount();
419     canvas->saveLayer(nullptr, nullptr);
420     canvas->restore();
421     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
422         testStep->assertMessage());
423 }
424 TEST_STEP(SaveLayer, SaveLayerStep);
425 
BoundedSaveLayerStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)426 static void BoundedSaveLayerStep(SkCanvas* canvas, const TestData& d,
427                                  skiatest::Reporter* reporter, CanvasTestStep* testStep) {
428     int saveCount = canvas->getSaveCount();
429     canvas->saveLayer(&d.fRect, nullptr);
430     canvas->restore();
431     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
432         testStep->assertMessage());
433 }
434 TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep);
435 
PaintSaveLayerStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)436 static void PaintSaveLayerStep(SkCanvas* canvas, const TestData& d,
437                                skiatest::Reporter* reporter, CanvasTestStep* testStep) {
438     int saveCount = canvas->getSaveCount();
439     canvas->saveLayer(nullptr, &d.fPaint);
440     canvas->restore();
441     REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
442         testStep->assertMessage());
443 }
444 TEST_STEP(PaintSaveLayer, PaintSaveLayerStep);
445 
TwoClipOpsStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)446 static void TwoClipOpsStep(SkCanvas* canvas, const TestData& d,
447                            skiatest::Reporter*, CanvasTestStep*) {
448     // This test exercises a functionality in SkPicture that leads to the
449     // recording of restore offset placeholders.  This test will trigger an
450     // assertion at playback time if the placeholders are not properly
451     // filled when the recording ends.
452     canvas->clipRect(d.fRect);
453     canvas->clipRegion(d.fRegion);
454 }
455 TEST_STEP(TwoClipOps, TwoClipOpsStep);
456 
457 // exercise fix for http://code.google.com/p/skia/issues/detail?id=560
458 // ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
DrawNearlyZeroLengthPathTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)459 static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas, const TestData& d,
460                                              skiatest::Reporter*, CanvasTestStep*) {
461     SkPaint paint;
462     paint.setStrokeWidth(SkIntToScalar(1));
463     paint.setStyle(SkPaint::kStroke_Style);
464 
465     canvas->drawPath(d.fNearlyZeroLengthPath, paint);
466 }
467 TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep);
468 
DrawVerticesShaderTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)469 static void DrawVerticesShaderTestStep(SkCanvas* canvas, const TestData& d,
470                                        skiatest::Reporter*, CanvasTestStep*) {
471     SkPoint pts[4];
472     pts[0].set(0, 0);
473     pts[1].set(SkIntToScalar(d.fWidth), 0);
474     pts[2].set(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight));
475     pts[3].set(0, SkIntToScalar(d.fHeight));
476     SkPaint paint;
477     paint.setShader(SkShader::MakeBitmapShader(d.fBitmap, SkShader::kClamp_TileMode,
478                                                SkShader::kClamp_TileMode));
479     canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
480                          nullptr, SkBlendMode::kModulate, nullptr, 0, paint);
481 }
482 // NYI: issue 240.
483 TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep);
484 
DrawPictureTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)485 static void DrawPictureTestStep(SkCanvas* canvas, const TestData& d,
486                                 skiatest::Reporter*, CanvasTestStep*) {
487     SkPictureRecorder recorder;
488     SkCanvas* testCanvas = recorder.beginRecording(SkIntToScalar(d.fWidth), SkIntToScalar(d.fHeight),
489                                                    nullptr, 0);
490     testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
491     testCanvas->clipRect(d.fRect);
492     testCanvas->drawRect(d.fRect, d.fPaint);
493 
494     canvas->drawPicture(recorder.finishRecordingAsPicture());
495 }
496 TEST_STEP(DrawPicture, DrawPictureTestStep);
497 
SaveRestoreTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)498 static void SaveRestoreTestStep(SkCanvas* canvas, const TestData& d,
499                                 skiatest::Reporter* reporter, CanvasTestStep* testStep) {
500     int baseSaveCount = canvas->getSaveCount();
501     int n = canvas->save();
502     REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount == n, testStep->assertMessage());
503     REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(),
504         testStep->assertMessage());
505     canvas->save();
506     canvas->save();
507     REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 3 == canvas->getSaveCount(),
508         testStep->assertMessage());
509     canvas->restoreToCount(baseSaveCount + 1);
510     REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(),
511         testStep->assertMessage());
512 
513     // should this pin to 1, or be a no-op, or crash?
514     canvas->restoreToCount(0);
515     REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(),
516         testStep->assertMessage());
517 }
518 TEST_STEP(SaveRestore, SaveRestoreTestStep);
519 
NestedSaveRestoreWithSolidPaintTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)520 static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas, const TestData& d,
521                                                     skiatest::Reporter*, CanvasTestStep*) {
522     // This test step challenges the TestDeferredCanvasStateConsistency
523     // test cases because the opaque paint can trigger an optimization
524     // that discards previously recorded commands. The challenge is to maintain
525     // correct clip and matrix stack state.
526     canvas->resetMatrix();
527     canvas->rotate(SkIntToScalar(30));
528     canvas->save();
529     canvas->translate(SkIntToScalar(2), SkIntToScalar(1));
530     canvas->save();
531     canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
532     SkPaint paint;
533     paint.setColor(0xFFFFFFFF);
534     canvas->drawPaint(paint);
535     canvas->restore();
536     canvas->restore();
537 }
538 TEST_STEP(NestedSaveRestoreWithSolidPaint, \
539     NestedSaveRestoreWithSolidPaintTestStep);
540 
NestedSaveRestoreWithFlushTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter *,CanvasTestStep *)541 static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas, const TestData& d,
542                                                skiatest::Reporter*, CanvasTestStep*) {
543     // This test step challenges the TestDeferredCanvasStateConsistency
544     // test case because the canvas flush on a deferred canvas will
545     // reset the recording session. The challenge is to maintain correct
546     // clip and matrix stack state on the playback canvas.
547     canvas->resetMatrix();
548     canvas->rotate(SkIntToScalar(30));
549     canvas->save();
550     canvas->translate(SkIntToScalar(2), SkIntToScalar(1));
551     canvas->save();
552     canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
553     canvas->drawRect(d.fRect,d.fPaint);
554     canvas->flush();
555     canvas->restore();
556     canvas->restore();
557 }
558 TEST_STEP(NestedSaveRestoreWithFlush, NestedSaveRestoreWithFlushTestStep);
559 
DescribeTopLayerTestStep(SkCanvas * canvas,const TestData & d,skiatest::Reporter * reporter,CanvasTestStep * testStep)560 static void DescribeTopLayerTestStep(SkCanvas* canvas,
561                                      const TestData& d,
562                                      skiatest::Reporter* reporter,
563                                      CanvasTestStep* testStep) {
564     SkMatrix m;
565     SkIRect r;
566     // NOTE: adjustToTopLayer() does *not* reduce the clip size, even if the canvas
567     // is smaller than 10x10!
568 
569     canvas->temporary_internal_describeTopLayer(&m, &r);
570     REPORTER_ASSERT_MESSAGE(reporter, m.isIdentity(), testStep->assertMessage());
571     REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 2, 2),
572                             testStep->assertMessage());
573 
574     // Putting a full-canvas layer on it should make no change to the results.
575     SkRect layerBounds = SkRect::MakeXYWH(0.f, 0.f, 10.f, 10.f);
576     canvas->saveLayer(layerBounds, nullptr);
577     canvas->temporary_internal_describeTopLayer(&m, &r);
578     REPORTER_ASSERT_MESSAGE(reporter, m.isIdentity(), testStep->assertMessage());
579     REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 2, 2),
580                             testStep->assertMessage());
581     canvas->restore();
582 
583     // Adding a translated layer translates the results.
584     // Default canvas is only 2x2, so can't offset our layer by very much at all;
585     // saveLayer() aborts if the bounds don't intersect.
586     layerBounds = SkRect::MakeXYWH(1.f, 1.f, 6.f, 6.f);
587     canvas->saveLayer(layerBounds, nullptr);
588     canvas->temporary_internal_describeTopLayer(&m, &r);
589     REPORTER_ASSERT_MESSAGE(reporter, m == SkMatrix::MakeTrans(-1.f, -1.f),
590                             testStep->assertMessage());
591     REPORTER_ASSERT_MESSAGE(reporter, r == SkIRect::MakeXYWH(0, 0, 1, 1),
592                             testStep->assertMessage());
593     canvas->restore();
594 
595 }
596 TEST_STEP(DescribeTopLayer, DescribeTopLayerTestStep);
597 
598 
TestPdfDevice(skiatest::Reporter * reporter,const TestData & d,CanvasTestStep * step)599 static void TestPdfDevice(skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* step) {
600     SkDynamicMemoryWStream outStream;
601     sk_sp<SkDocument> doc(SkDocument::MakePDF(&outStream));
602     REPORTER_ASSERT(reporter, doc);
603     if (!doc) {
604         return;
605     }
606     SkCanvas* canvas = doc->beginPage(SkIntToScalar(d.fWidth),
607                                       SkIntToScalar(d.fHeight));
608     REPORTER_ASSERT(reporter, canvas);
609     step->setAssertMessageFormat(kPdfAssertMessageFormat);
610     step->draw(canvas, d, reporter);
611 }
612 
613 /*
614  * This sub-test verifies that the test step passes when executed
615  * with SkCanvas and with classes derrived from SkCanvas. It also verifies
616  * that the all canvas derivatives report the same state as an SkCanvas
617  * after having executed the test step.
618  */
TestOverrideStateConsistency(skiatest::Reporter * reporter,const TestData & d,CanvasTestStep * testStep)619 static void TestOverrideStateConsistency(skiatest::Reporter* reporter, const TestData& d,
620                                          CanvasTestStep* testStep) {
621     SkBitmap referenceStore;
622     createBitmap(&referenceStore, 0xFFFFFFFF);
623     SkCanvas referenceCanvas(referenceStore);
624     testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
625     testStep->draw(&referenceCanvas, d, reporter);
626 }
627 
test_newraster(skiatest::Reporter * reporter)628 static void test_newraster(skiatest::Reporter* reporter) {
629     SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
630     const size_t minRowBytes = info.minRowBytes();
631     const size_t size = info.getSafeSize(minRowBytes);
632     SkAutoTMalloc<SkPMColor> storage(size);
633     SkPMColor* baseAddr = storage.get();
634     sk_bzero(baseAddr, size);
635 
636     std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
637     REPORTER_ASSERT(reporter, canvas);
638 
639     SkPixmap pmap;
640     const SkPMColor* addr = canvas->peekPixels(&pmap) ? pmap.addr32() : nullptr;
641     REPORTER_ASSERT(reporter, addr);
642     REPORTER_ASSERT(reporter, info == pmap.info());
643     REPORTER_ASSERT(reporter, minRowBytes == pmap.rowBytes());
644     for (int y = 0; y < info.height(); ++y) {
645         for (int x = 0; x < info.width(); ++x) {
646             REPORTER_ASSERT(reporter, 0 == addr[x]);
647         }
648         addr = (const SkPMColor*)((const char*)addr + pmap.rowBytes());
649     }
650 
651     // now try a deliberately bad info
652     info = info.makeWH(-1, info.height());
653     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
654 
655     // too big
656     info = info.makeWH(1 << 30, 1 << 30);
657     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
658 
659     // not a valid pixel type
660     info = SkImageInfo::Make(10, 10, kUnknown_SkColorType, info.alphaType());
661     REPORTER_ASSERT(reporter, nullptr == SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes));
662 
663     // We should succeed with a zero-sized valid info
664     info = SkImageInfo::MakeN32Premul(0, 0);
665     canvas = SkCanvas::MakeRasterDirect(info, baseAddr, minRowBytes);
666     REPORTER_ASSERT(reporter, canvas);
667 }
668 
DEF_TEST(Canvas,reporter)669 DEF_TEST(Canvas, reporter) {
670     TestData d;
671 
672     for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
673         TestOverrideStateConsistency(reporter, d, testStepArray()[testStep]);
674         if (testStepArray()[testStep]->enablePdfTesting()) {
675             TestPdfDevice(reporter, d, testStepArray()[testStep]);
676         }
677     }
678 
679     test_newraster(reporter);
680 }
681 
DEF_TEST(Canvas_SaveState,reporter)682 DEF_TEST(Canvas_SaveState, reporter) {
683     SkCanvas canvas(10, 10);
684     REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
685 
686     int n = canvas.save();
687     REPORTER_ASSERT(reporter, 1 == n);
688     REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
689 
690     n = canvas.saveLayer(nullptr, nullptr);
691     REPORTER_ASSERT(reporter, 2 == n);
692     REPORTER_ASSERT(reporter, 3 == canvas.getSaveCount());
693 
694     canvas.restore();
695     REPORTER_ASSERT(reporter, 2 == canvas.getSaveCount());
696     canvas.restore();
697     REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
698 }
699 
DEF_TEST(Canvas_ClipEmptyPath,reporter)700 DEF_TEST(Canvas_ClipEmptyPath, reporter) {
701     SkCanvas canvas(10, 10);
702     canvas.save();
703     SkPath path;
704     canvas.clipPath(path);
705     canvas.restore();
706     canvas.save();
707     path.moveTo(5, 5);
708     canvas.clipPath(path);
709     canvas.restore();
710     canvas.save();
711     path.moveTo(7, 7);
712     canvas.clipPath(path);  // should not assert here
713     canvas.restore();
714 }
715 
716 #define SHADOW_TEST_CANVAS_CONST 10
717 #ifdef SK_EXPERIMENTAL_SHADOWING
718 class SkShadowTestCanvas : public SkPaintFilterCanvas {
719 public:
720 
SkShadowTestCanvas(int x,int y,skiatest::Reporter * reporter)721     SkShadowTestCanvas(int x, int y, skiatest::Reporter* reporter)
722         : INHERITED(x,y)
723         , fReporter(reporter) {}
724 
onFilter(SkTCopyOnFirstWrite<SkPaint> * paint,Type type) const725     bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const {
726         REPORTER_ASSERT(this->fReporter, this->getZ() == SHADOW_TEST_CANVAS_CONST);
727 
728         return true;
729     }
730 
testUpdateDepth(skiatest::Reporter * reporter)731     void testUpdateDepth(skiatest::Reporter *reporter) {
732         // set some depths (with picture enabled), then check them as they get set
733 
734         REPORTER_ASSERT(reporter, this->getZ() == 0);
735         this->translateZ(-10);
736         REPORTER_ASSERT(reporter, this->getZ() == -10);
737 
738         this->save();
739         this->translateZ(20);
740         REPORTER_ASSERT(reporter, this->getZ() == 10);
741 
742         this->restore();
743         REPORTER_ASSERT(reporter, this->getZ() == -10);
744 
745         this->translateZ(13.14f);
746         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(this->getZ(), 3.14f));
747     }
748 
749 private:
750     skiatest::Reporter* fReporter;
751 
752     typedef SkPaintFilterCanvas INHERITED;
753 };
754 #endif
755 
756 namespace {
757 
758 class MockFilterCanvas : public SkPaintFilterCanvas {
759 public:
MockFilterCanvas(SkCanvas * canvas)760     MockFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
761 
762 protected:
onFilter(SkTCopyOnFirstWrite<SkPaint> *,Type) const763     bool onFilter(SkTCopyOnFirstWrite<SkPaint>*, Type) const override { return true; }
764 
765 private:
766     typedef SkPaintFilterCanvas INHERITED;
767 };
768 
769 } // anonymous namespace
770 
771 // SkPaintFilterCanvas should inherit the initial target canvas state.
DEF_TEST(PaintFilterCanvas_ConsistentState,reporter)772 DEF_TEST(PaintFilterCanvas_ConsistentState, reporter) {
773     SkCanvas canvas(100, 100);
774     canvas.clipRect(SkRect::MakeXYWH(12.7f, 12.7f, 75, 75));
775     canvas.scale(0.5f, 0.75f);
776 
777     MockFilterCanvas filterCanvas(&canvas);
778     REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
779     REPORTER_ASSERT(reporter, canvas.getLocalClipBounds() == filterCanvas.getLocalClipBounds());
780 
781     filterCanvas.clipRect(SkRect::MakeXYWH(30.5f, 30.7f, 100, 100));
782     filterCanvas.scale(0.75f, 0.5f);
783     REPORTER_ASSERT(reporter, canvas.getTotalMatrix() == filterCanvas.getTotalMatrix());
784     REPORTER_ASSERT(reporter, filterCanvas.getLocalClipBounds().contains(canvas.getLocalClipBounds()));
785 
786 #ifdef SK_EXPERIMENTAL_SHADOWING
787     SkShadowTestCanvas* tCanvas = new SkShadowTestCanvas(100,100, reporter);
788     tCanvas->testUpdateDepth(reporter);
789     delete(tCanvas);
790 
791     SkPictureRecorder recorder;
792     SkShadowTestCanvas *tSCanvas = new SkShadowTestCanvas(100, 100, reporter);
793     SkCanvas *tPCanvas = recorder.beginRecording(SkRect::MakeIWH(100, 100));
794 
795     tPCanvas->translateZ(SHADOW_TEST_CANVAS_CONST);
796     sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
797     tSCanvas->drawPicture(pic);
798 
799     delete(tSCanvas);
800 #endif
801 }
802 
803 ///////////////////////////////////////////////////////////////////////////////////////////////////
804 
805 #include "SkCanvasStack.h"
806 #include "SkNWayCanvas.h"
807 
808 // Subclass that takes a bool*, which it updates in its construct (true) and destructor (false)
809 // to allow the caller to know how long the object is alive.
810 class LifeLineCanvas : public SkCanvas {
811     bool*   fLifeLine;
812 public:
LifeLineCanvas(int w,int h,bool * lifeline)813     LifeLineCanvas(int w, int h, bool* lifeline) : SkCanvas(w, h), fLifeLine(lifeline) {
814         *fLifeLine = true;
815     }
~LifeLineCanvas()816     ~LifeLineCanvas() {
817         *fLifeLine = false;
818     }
819 };
820 
821 // Check that NWayCanvas does NOT try to manage the lifetime of its sub-canvases
DEF_TEST(NWayCanvas,r)822 DEF_TEST(NWayCanvas, r) {
823     const int w = 10;
824     const int h = 10;
825     bool life[2];
826     {
827         LifeLineCanvas c0(w, h, &life[0]);
828         REPORTER_ASSERT(r, life[0]);
829     }
830     REPORTER_ASSERT(r, !life[0]);
831 
832 
833     std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
834     std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
835     REPORTER_ASSERT(r, life[0]);
836     REPORTER_ASSERT(r, life[1]);
837 
838     {
839         SkNWayCanvas nway(w, h);
840         nway.addCanvas(c0.get());
841         nway.addCanvas(c1.get());
842         REPORTER_ASSERT(r, life[0]);
843         REPORTER_ASSERT(r, life[1]);
844     }
845     // Now assert that the death of the nway has NOT also killed the sub-canvases
846     REPORTER_ASSERT(r, life[0]);
847     REPORTER_ASSERT(r, life[1]);
848 }
849 
850 // Check that CanvasStack DOES manage the lifetime of its sub-canvases
DEF_TEST(CanvasStack,r)851 DEF_TEST(CanvasStack, r) {
852     const int w = 10;
853     const int h = 10;
854     bool life[2];
855     std::unique_ptr<SkCanvas> c0 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[0]));
856     std::unique_ptr<SkCanvas> c1 = std::unique_ptr<SkCanvas>(new LifeLineCanvas(w, h, &life[1]));
857     REPORTER_ASSERT(r, life[0]);
858     REPORTER_ASSERT(r, life[1]);
859 
860     {
861         SkCanvasStack stack(w, h);
862         stack.pushCanvas(std::move(c0), {0,0});
863         stack.pushCanvas(std::move(c1), {0,0});
864         REPORTER_ASSERT(r, life[0]);
865         REPORTER_ASSERT(r, life[1]);
866     }
867     // Now assert that the death of the canvasstack has also killed the sub-canvases
868     REPORTER_ASSERT(r, !life[0]);
869     REPORTER_ASSERT(r, !life[1]);
870 }
871 
test_cliptype(SkCanvas * canvas,skiatest::Reporter * r)872 static void test_cliptype(SkCanvas* canvas, skiatest::Reporter* r) {
873     REPORTER_ASSERT(r, !canvas->isClipEmpty());
874     REPORTER_ASSERT(r, canvas->isClipRect());
875 
876     canvas->save();
877     canvas->clipRect({0, 0, 0, 0});
878     REPORTER_ASSERT(r, canvas->isClipEmpty());
879     REPORTER_ASSERT(r, !canvas->isClipRect());
880     canvas->restore();
881 
882     canvas->save();
883     canvas->clipRect({2, 2, 6, 6});
884     REPORTER_ASSERT(r, !canvas->isClipEmpty());
885     REPORTER_ASSERT(r, canvas->isClipRect());
886     canvas->restore();
887 
888     canvas->save();
889     canvas->clipRect({2, 2, 6, 6}, SkClipOp::kDifference);  // punch a hole in the clip
890     REPORTER_ASSERT(r, !canvas->isClipEmpty());
891     REPORTER_ASSERT(r, !canvas->isClipRect());
892     canvas->restore();
893 
894     REPORTER_ASSERT(r, !canvas->isClipEmpty());
895     REPORTER_ASSERT(r, canvas->isClipRect());
896 }
897 
DEF_TEST(CanvasClipType,r)898 DEF_TEST(CanvasClipType, r) {
899     // test rasterclip backend
900     test_cliptype(SkSurface::MakeRasterN32Premul(10, 10)->getCanvas(), r);
901 
902     // test clipstack backend
903     SkDynamicMemoryWStream stream;
904     test_cliptype(SkDocument::MakePDF(&stream)->beginPage(100, 100), r);
905 }
906 
907 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
DEF_TEST(Canvas_LegacyColorBehavior,r)908 DEF_TEST(Canvas_LegacyColorBehavior, r) {
909     sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
910                                                    SkColorSpace::kAdobeRGB_Gamut);
911 
912     // Make a Adobe RGB bitmap.
913     SkBitmap bitmap;
914     bitmap.allocPixels(SkImageInfo::MakeN32(1, 1, kOpaque_SkAlphaType, cs));
915     bitmap.eraseColor(0xFF000000);
916 
917     // Wrap it in a legacy canvas.  Test that the canvas behaves like a legacy canvas.
918     SkCanvas canvas(bitmap, SkCanvas::ColorBehavior::kLegacy);
919     REPORTER_ASSERT(r, !canvas.imageInfo().colorSpace());
920     SkPaint p;
921     p.setColor(SK_ColorRED);
922     canvas.drawIRect(SkIRect::MakeWH(1, 1), p);
923     REPORTER_ASSERT(r, SK_ColorRED == SkSwizzle_BGRA_to_PMColor(*bitmap.getAddr32(0, 0)));
924 }
925 #endif
926