1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint3.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkVertices.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/utils/SkShadowUtils.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkDrawShadowInfo.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkVerticesPriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkShadowTessellator.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker enum ExpectVerts {
29*c8dee2aaSAndroid Build Coastguard Worker kDont_ExpectVerts,
30*c8dee2aaSAndroid Build Coastguard Worker kDo_ExpectVerts
31*c8dee2aaSAndroid Build Coastguard Worker };
32*c8dee2aaSAndroid Build Coastguard Worker
check_result(skiatest::Reporter * reporter,sk_sp<SkVertices> verts,ExpectVerts expectVerts,bool expectSuccess)33*c8dee2aaSAndroid Build Coastguard Worker void check_result(skiatest::Reporter* reporter, sk_sp<SkVertices> verts,
34*c8dee2aaSAndroid Build Coastguard Worker ExpectVerts expectVerts, bool expectSuccess) {
35*c8dee2aaSAndroid Build Coastguard Worker if (expectSuccess != SkToBool(verts)) {
36*c8dee2aaSAndroid Build Coastguard Worker ERRORF(reporter, "Expected shadow tessellation to %s but it did not.",
37*c8dee2aaSAndroid Build Coastguard Worker expectSuccess ? "succeed" : "fail");
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker if (SkToBool(verts)) {
40*c8dee2aaSAndroid Build Coastguard Worker if (kDont_ExpectVerts == expectVerts && verts->priv().vertexCount()) {
41*c8dee2aaSAndroid Build Coastguard Worker ERRORF(reporter, "Expected shadow tessellation to generate no vertices but it did.");
42*c8dee2aaSAndroid Build Coastguard Worker } else if (kDo_ExpectVerts == expectVerts && !verts->priv().vertexCount()) {
43*c8dee2aaSAndroid Build Coastguard Worker ERRORF(reporter, "Expected shadow tessellation to generate vertices but it didn't.");
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker
tessellate_shadow(skiatest::Reporter * reporter,const SkPath & path,const SkMatrix & ctm,const SkPoint3 & heightParams,ExpectVerts expectVerts,bool expectSuccess)48*c8dee2aaSAndroid Build Coastguard Worker void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm,
49*c8dee2aaSAndroid Build Coastguard Worker const SkPoint3& heightParams, ExpectVerts expectVerts, bool expectSuccess) {
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true);
52*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false);
55*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, true, false);
58*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false,
61*c8dee2aaSAndroid Build Coastguard Worker false);
62*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, true, true);
65*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, false, true);
68*c8dee2aaSAndroid Build Coastguard Worker check_result(reporter, verts, expectVerts, expectSuccess);
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(ShadowUtils,reporter)71*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ShadowUtils, reporter) {
72*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(100, 100);
73*c8dee2aaSAndroid Build Coastguard Worker
74*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
75*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(100, 50, 20, 100, 0, 0);
76*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDo_ExpectVerts, true);
77*c8dee2aaSAndroid Build Coastguard Worker // super high path
78*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4.0e+37f},
79*c8dee2aaSAndroid Build Coastguard Worker kDo_ExpectVerts, true);
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker // This line segment has no area and no shadow.
82*c8dee2aaSAndroid Build Coastguard Worker path.reset();
83*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(10.f, 10.f);
84*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDont_ExpectVerts, true);
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker // A series of collinear line segments
87*c8dee2aaSAndroid Build Coastguard Worker path.reset();
88*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 10; ++i) {
89*c8dee2aaSAndroid Build Coastguard Worker path.lineTo((SkScalar)i, (SkScalar)i);
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDont_ExpectVerts, true);
92*c8dee2aaSAndroid Build Coastguard Worker
93*c8dee2aaSAndroid Build Coastguard Worker // ugly degenerate path
94*c8dee2aaSAndroid Build Coastguard Worker path.reset();
95*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(-134217728, 2.22265153e+21f);
96*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(-2.33326106e+21f, 7.36298265e-41f, 3.72237738e-22f, 5.99502692e-36f,
97*c8dee2aaSAndroid Build Coastguard Worker 1.13631943e+22f, 2.0890786e+33f);
98*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(1.03397626e-25f, 5.99502692e-36f, 9.18354962e-41f, 0, 4.6142745e-37f, -213558848);
99*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-134217728, 2.2226515e+21f);
100*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, true);
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker // simple concave path (star of David)
103*c8dee2aaSAndroid Build Coastguard Worker path.reset();
104*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(0.0f, -50.0f);
105*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(14.43f, -25.0f);
106*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(43.30f, -25.0f);
107*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(28.86f, 0.0f);
108*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(43.30f, 25.0f);
109*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(14.43f, 25.0f);
110*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(0.0f, 50.0f);
111*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-14.43f, 25.0f);
112*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-43.30f, 25.0f);
113*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-28.86f, 0.0f);
114*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-43.30f, -25.0f);
115*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-14.43f, -25.0f);
116*c8dee2aaSAndroid Build Coastguard Worker // uncomment when transparent concave shadows are working
117*c8dee2aaSAndroid Build Coastguard Worker // tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDo_ExpectVerts, true);
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker // complex concave path (bowtie)
120*c8dee2aaSAndroid Build Coastguard Worker path.reset();
121*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(-50, -50);
122*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-50, 50);
123*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(50, -50);
124*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(50, 50);
125*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(-50, -50);
126*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, false);
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker // multiple contour path
129*c8dee2aaSAndroid Build Coastguard Worker path.close();
130*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(0, 0);
131*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(1, 0);
132*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(0, 1);
133*c8dee2aaSAndroid Build Coastguard Worker tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 9}, kDont_ExpectVerts, false);
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker
check_xformed_bounds(skiatest::Reporter * reporter,const SkPath & path,const SkMatrix & ctm)136*c8dee2aaSAndroid Build Coastguard Worker void check_xformed_bounds(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm) {
137*c8dee2aaSAndroid Build Coastguard Worker SkDrawShadowRec rec = {
138*c8dee2aaSAndroid Build Coastguard Worker SkPoint3::Make(0, 0, 4),
139*c8dee2aaSAndroid Build Coastguard Worker SkPoint3::Make(100, 0, 600),
140*c8dee2aaSAndroid Build Coastguard Worker 800.f,
141*c8dee2aaSAndroid Build Coastguard Worker 0x08000000,
142*c8dee2aaSAndroid Build Coastguard Worker 0x40000000,
143*c8dee2aaSAndroid Build Coastguard Worker 0
144*c8dee2aaSAndroid Build Coastguard Worker };
145*c8dee2aaSAndroid Build Coastguard Worker // point light
146*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds;
147*c8dee2aaSAndroid Build Coastguard Worker SkDrawShadowMetrics::GetLocalBounds(path, rec, ctm, &bounds);
148*c8dee2aaSAndroid Build Coastguard Worker ctm.mapRect(&bounds);
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker auto verts = SkShadowTessellator::MakeAmbient(path, ctm, rec.fZPlaneParams, true);
151*c8dee2aaSAndroid Build Coastguard Worker if (verts) {
152*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker SkPoint mapXY = ctm.mapXY(rec.fLightPos.fX, rec.fLightPos.fY);
156*c8dee2aaSAndroid Build Coastguard Worker SkPoint3 devLightPos = SkPoint3::Make(mapXY.fX, mapXY.fY, rec.fLightPos.fZ);
157*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, rec.fZPlaneParams, devLightPos,
158*c8dee2aaSAndroid Build Coastguard Worker rec.fLightRadius, false, false);
159*c8dee2aaSAndroid Build Coastguard Worker if (verts) {
160*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
161*c8dee2aaSAndroid Build Coastguard Worker }
162*c8dee2aaSAndroid Build Coastguard Worker
163*c8dee2aaSAndroid Build Coastguard Worker // directional light
164*c8dee2aaSAndroid Build Coastguard Worker rec.fFlags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
165*c8dee2aaSAndroid Build Coastguard Worker rec.fLightRadius = 2.0f;
166*c8dee2aaSAndroid Build Coastguard Worker SkDrawShadowMetrics::GetLocalBounds(path, rec, ctm, &bounds);
167*c8dee2aaSAndroid Build Coastguard Worker ctm.mapRect(&bounds);
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeAmbient(path, ctm, rec.fZPlaneParams, true);
170*c8dee2aaSAndroid Build Coastguard Worker if (verts) {
171*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
172*c8dee2aaSAndroid Build Coastguard Worker }
173*c8dee2aaSAndroid Build Coastguard Worker
174*c8dee2aaSAndroid Build Coastguard Worker devLightPos = rec.fLightPos;
175*c8dee2aaSAndroid Build Coastguard Worker devLightPos.normalize();
176*c8dee2aaSAndroid Build Coastguard Worker verts = SkShadowTessellator::MakeSpot(path, ctm, rec.fZPlaneParams, devLightPos,
177*c8dee2aaSAndroid Build Coastguard Worker rec.fLightRadius, false, true);
178*c8dee2aaSAndroid Build Coastguard Worker if (verts) {
179*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker }
182*c8dee2aaSAndroid Build Coastguard Worker
check_bounds(skiatest::Reporter * reporter,const SkPath & path)183*c8dee2aaSAndroid Build Coastguard Worker void check_bounds(skiatest::Reporter* reporter, const SkPath& path) {
184*c8dee2aaSAndroid Build Coastguard Worker const bool fixed_shadows_in_perspective = false; // skbug.com/9698
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker SkMatrix ctm;
187*c8dee2aaSAndroid Build Coastguard Worker ctm.setTranslate(100, 100);
188*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
189*c8dee2aaSAndroid Build Coastguard Worker ctm.postScale(2, 2);
190*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
191*c8dee2aaSAndroid Build Coastguard Worker ctm.preRotate(45);
192*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
193*c8dee2aaSAndroid Build Coastguard Worker ctm.preSkew(40, -20);
194*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
195*c8dee2aaSAndroid Build Coastguard Worker if (fixed_shadows_in_perspective) {
196*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp0] = 0.0001f;
197*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp1] = 12.f;
198*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
199*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp0] = 0.0001f;
200*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp1] = -12.f;
201*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
202*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp0] = 12.f;
203*c8dee2aaSAndroid Build Coastguard Worker ctm[SkMatrix::kMPersp1] = 0.0001f;
204*c8dee2aaSAndroid Build Coastguard Worker check_xformed_bounds(reporter, path, ctm);
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(ShadowBounds,reporter)208*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ShadowBounds, reporter) {
209*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
210*c8dee2aaSAndroid Build Coastguard Worker path.addRRect(SkRRect::MakeRectXY(SkRect::MakeLTRB(-50, -20, 40, 30), 4, 4));
211*c8dee2aaSAndroid Build Coastguard Worker check_bounds(reporter, path);
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker path.reset();
214*c8dee2aaSAndroid Build Coastguard Worker path.addOval(SkRect::MakeLTRB(300, 300, 900, 900));
215*c8dee2aaSAndroid Build Coastguard Worker check_bounds(reporter, path);
216*c8dee2aaSAndroid Build Coastguard Worker
217*c8dee2aaSAndroid Build Coastguard Worker path.reset();
218*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(100, 50, 20, 100, 0, 0);
219*c8dee2aaSAndroid Build Coastguard Worker check_bounds(reporter, path);
220*c8dee2aaSAndroid Build Coastguard Worker }
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker #endif // !defined(SK_ENABLE_OPTIMIZE_SIZE)
223