xref: /aosp_15_r20/external/skia/tests/ShadowTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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