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