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