• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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 "SkCanvas.h"
9 #include "SkDrawShadowInfo.h"
10 #include "SkPath.h"
11 #include "SkShadowTessellator.h"
12 #include "SkShadowUtils.h"
13 #include "SkVertices.h"
14 #include "Test.h"
15 
16 enum ExpectVerts {
17     kDont_ExpectVerts,
18     kDo_ExpectVerts
19 };
20 
check_result(skiatest::Reporter * reporter,sk_sp<SkVertices> verts,ExpectVerts expectVerts,bool expectSuccess)21 void check_result(skiatest::Reporter* reporter, sk_sp<SkVertices> verts,
22                   ExpectVerts expectVerts, bool expectSuccess) {
23     if (expectSuccess != SkToBool(verts)) {
24         ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
25                expectSuccess ? "succeed" : "fail");
26     }
27     if (SkToBool(verts)) {
28         if (kDont_ExpectVerts == expectVerts && verts->vertexCount()) {
29             ERRORF(reporter, "Expected shadow tessellation to generate no vertices but it did.");
30         } else if (kDo_ExpectVerts == expectVerts && !verts->vertexCount()) {
31             ERRORF(reporter, "Expected shadow tessellation to generate vertices but it didn't.");
32         }
33     }
34 }
35 
tessellate_shadow(skiatest::Reporter * reporter,const SkPath & path,const SkMatrix & ctm,const SkPoint3 & heightParams,ExpectVerts expectVerts,bool expectSuccess)36 void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm,
37                        const SkPoint3& heightParams, ExpectVerts expectVerts, bool expectSuccess) {
38 
39     auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true);
40     check_result(reporter, verts, expectVerts, expectSuccess);
41 
42     verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false);
43     check_result(reporter, verts, expectVerts, expectSuccess);
44 
45     verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
46     check_result(reporter, verts, expectVerts, expectSuccess);
47 
48     verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false);
49     check_result(reporter, verts, expectVerts, expectSuccess);
50 }
51 
DEF_TEST(ShadowUtils,reporter)52 DEF_TEST(ShadowUtils, reporter) {
53     SkCanvas canvas(100, 100);
54 
55     SkPath path;
56     path.cubicTo(100, 50, 20, 100, 0, 0);
57     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDo_ExpectVerts, true);
58     // super high path
59     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4.0e+37f},
60                       kDo_ExpectVerts, true);
61 
62     // This line segment has no area and no shadow.
63     path.reset();
64     path.lineTo(10.f, 10.f);
65     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDont_ExpectVerts, true);
66 
67     // A series of collinear line segments
68     path.reset();
69     for (int i = 0; i < 10; ++i) {
70         path.lineTo((SkScalar)i, (SkScalar)i);
71     }
72     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDont_ExpectVerts, true);
73 
74     // ugly degenerate path
75     path.reset();
76     path.moveTo(-134217728, 2.22265153e+21f);
77     path.cubicTo(-2.33326106e+21f, 7.36298265e-41f, 3.72237738e-22f, 5.99502692e-36f,
78                  1.13631943e+22f, 2.0890786e+33f);
79     path.cubicTo(1.03397626e-25f, 5.99502692e-36f, 9.18354962e-41f, 0, 4.6142745e-37f, -213558848);
80     path.lineTo(-134217728, 2.2226515e+21f);
81     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, true);
82 
83     // simple concave path (star of David)
84     path.reset();
85     path.moveTo(0.0f, -50.0f);
86     path.lineTo(14.43f, -25.0f);
87     path.lineTo(43.30f, -25.0f);
88     path.lineTo(28.86f, 0.0f);
89     path.lineTo(43.30f, 25.0f);
90     path.lineTo(14.43f, 25.0f);
91     path.lineTo(0.0f, 50.0f);
92     path.lineTo(-14.43f, 25.0f);
93     path.lineTo(-43.30f, 25.0f);
94     path.lineTo(-28.86f, 0.0f);
95     path.lineTo(-43.30f, -25.0f);
96     path.lineTo(-14.43f, -25.0f);
97 // uncomment when transparent concave shadows are working
98 //    tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDo_ExpectVerts, true);
99 
100     // complex concave path (bowtie)
101     path.reset();
102     path.moveTo(-50, -50);
103     path.lineTo(-50, 50);
104     path.lineTo(50, -50);
105     path.lineTo(50, 50);
106     path.lineTo(-50, -50);
107     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, false);
108 
109     // multiple contour path
110     path.close();
111     path.moveTo(0, 0);
112     path.lineTo(1, 0);
113     path.lineTo(0, 1);
114     tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, false);
115 }
116 
check_xformed_bounds(skiatest::Reporter * reporter,const SkPath & path,const SkMatrix & ctm)117 void check_xformed_bounds(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm) {
118     const SkDrawShadowRec rec = {
119         SkPoint3::Make(0, 0, 4),
120         SkPoint3::Make(100, 0, 600),
121         800.f,
122         0x08000000,
123         0x40000000,
124         0
125     };
126     SkRect bounds;
127     SkDrawShadowMetrics::GetLocalBounds(path, rec, ctm, &bounds);
128     ctm.mapRect(&bounds);
129 
130     auto verts = SkShadowTessellator::MakeAmbient(path, ctm, rec.fZPlaneParams, true);
131     if (verts) {
132         REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
133     }
134 
135     SkPoint mapXY = ctm.mapXY(rec.fLightPos.fX, rec.fLightPos.fY);
136     SkPoint3 devLightPos = SkPoint3::Make(mapXY.fX, mapXY.fY, rec.fLightPos.fZ);
137     verts = SkShadowTessellator::MakeSpot(path, ctm, rec.fZPlaneParams, devLightPos,
138                                           rec.fLightRadius, false);
139     if (verts) {
140         REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
141     }
142 }
143 
check_bounds(skiatest::Reporter * reporter,const SkPath & path)144 void check_bounds(skiatest::Reporter* reporter, const SkPath& path) {
145     SkMatrix ctm;
146     ctm.setTranslate(100, 100);
147     check_xformed_bounds(reporter, path, ctm);
148     ctm.postScale(2, 2);
149     check_xformed_bounds(reporter, path, ctm);
150     ctm.preRotate(45);
151     check_xformed_bounds(reporter, path, ctm);
152     ctm.preSkew(40, -20);
153     check_xformed_bounds(reporter, path, ctm);
154     ctm[SkMatrix::kMPersp0] = 0.0001f;
155     ctm[SkMatrix::kMPersp1] = 12.f;
156     check_xformed_bounds(reporter, path, ctm);
157     ctm[SkMatrix::kMPersp0] = 0.0001f;
158     ctm[SkMatrix::kMPersp1] = -12.f;
159     check_xformed_bounds(reporter, path, ctm);
160     ctm[SkMatrix::kMPersp0] = 12.f;
161     ctm[SkMatrix::kMPersp1] = 0.0001f;
162     check_xformed_bounds(reporter, path, ctm);
163 }
164 
DEF_TEST(ShadowBounds,reporter)165 DEF_TEST(ShadowBounds, reporter) {
166     SkPath path;
167     path.addRRect(SkRRect::MakeRectXY(SkRect::MakeLTRB(-50, -20, 40, 30), 4, 4));
168     check_bounds(reporter, path);
169 
170     path.reset();
171     path.addOval(SkRect::MakeLTRB(300, 300, 900, 900));
172     check_bounds(reporter, path);
173 
174     path.reset();
175     path.cubicTo(100, 50, 20, 100, 0, 0);
176     check_bounds(reporter, path);
177 }
178