blob: 7217c8f2b8f383188d887a0ec9828af0ffb740b5 [file] [log] [blame]
Jim Van Verth1af03d42017-07-31 09:34:58 -04001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Mike Reedba962562020-03-12 20:33:21 -040013#include "src/core/SkVerticesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/utils/SkShadowTessellator.h"
15#include "tests/Test.h"
Jim Van Verth1af03d42017-07-31 09:34:58 -040016
Jim Van Verth3645bb02018-06-26 14:58:58 -040017enum ExpectVerts {
18 kDont_ExpectVerts,
19 kDo_ExpectVerts
20};
Jim Van Verth1af03d42017-07-31 09:34:58 -040021
Jim Van Verth3645bb02018-06-26 14:58:58 -040022void 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)) {
Brian Osman8cbedf92020-03-31 10:38:31 -040029 if (kDont_ExpectVerts == expectVerts && verts->priv().vertexCount()) {
Jim Van Verth3645bb02018-06-26 14:58:58 -040030 ERRORF(reporter, "Expected shadow tessellation to generate no vertices but it did.");
Brian Osman8cbedf92020-03-31 10:38:31 -040031 } else if (kDo_ExpectVerts == expectVerts && !verts->priv().vertexCount()) {
Jim Van Verth3645bb02018-06-26 14:58:58 -040032 ERRORF(reporter, "Expected shadow tessellation to generate vertices but it didn't.");
33 }
34 }
35}
36
37void tessellate_shadow(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm,
38 const SkPoint3& heightParams, ExpectVerts expectVerts, bool expectSuccess) {
Jim Van Verth1af03d42017-07-31 09:34:58 -040039
40 auto verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, true);
Jim Van Verth3645bb02018-06-26 14:58:58 -040041 check_result(reporter, verts, expectVerts, expectSuccess);
42
Jim Van Verth1af03d42017-07-31 09:34:58 -040043 verts = SkShadowTessellator::MakeAmbient(path, ctm, heightParams, false);
Jim Van Verth3645bb02018-06-26 14:58:58 -040044 check_result(reporter, verts, expectVerts, expectSuccess);
45
Jim Van Verth63f03542020-12-16 11:56:11 -050046 verts = SkShadowTessellator::MakeSpot(path, ctm, heightParams, {0, 0, 128}, 128.f, true, false);
Jim Van Verth3645bb02018-06-26 14:58:58 -040047 check_result(reporter, verts, expectVerts, expectSuccess);
48
Jim Van Verth63f03542020-12-16 11:56:11 -050049 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);
Jim Van Verth3645bb02018-06-26 14:58:58 -040057 check_result(reporter, verts, expectVerts, expectSuccess);
Jim Van Verth1af03d42017-07-31 09:34:58 -040058}
59
60DEF_TEST(ShadowUtils, reporter) {
61 SkCanvas canvas(100, 100);
62
63 SkPath path;
64 path.cubicTo(100, 50, 20, 100, 0, 0);
Jim Van Verth3645bb02018-06-26 14:58:58 -040065 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);
Jim Van Verth1af03d42017-07-31 09:34:58 -040069
70 // This line segment has no area and no shadow.
71 path.reset();
72 path.lineTo(10.f, 10.f);
Jim Van Verth3645bb02018-06-26 14:58:58 -040073 tessellate_shadow(reporter, path, canvas.getTotalMatrix(), {0, 0, 4}, kDont_ExpectVerts, true);
Jim Van Verth1af03d42017-07-31 09:34:58 -040074
Jim Van Verth3645bb02018-06-26 14:58:58 -040075 // A series of collinear line segments
Jim Van Verth1af03d42017-07-31 09:34:58 -040076 path.reset();
77 for (int i = 0; i < 10; ++i) {
78 path.lineTo((SkScalar)i, (SkScalar)i);
79 }
Jim Van Verth3645bb02018-06-26 14:58:58 -040080 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);
Jim Van Verth1af03d42017-07-31 09:34:58 -0400123}
124
125void check_xformed_bounds(skiatest::Reporter* reporter, const SkPath& path, const SkMatrix& ctm) {
Jim Van Vertha8682202020-12-17 10:18:16 -0500126 SkDrawShadowRec rec = {
Jim Van Verth1af03d42017-07-31 09:34:58 -0400127 SkPoint3::Make(0, 0, 4),
128 SkPoint3::Make(100, 0, 600),
129 800.f,
Jim Van Verthb1b80f72018-01-18 15:19:13 -0500130 0x08000000,
131 0x40000000,
Jim Van Verth1af03d42017-07-31 09:34:58 -0400132 0
133 };
Jim Van Vertha8682202020-12-17 10:18:16 -0500134 // point light
Jim Van Verth1af03d42017-07-31 09:34:58 -0400135 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,
Jim Van Verth63f03542020-12-16 11:56:11 -0500147 rec.fLightRadius, false, false);
Jim Van Verth1af03d42017-07-31 09:34:58 -0400148 if (verts) {
149 REPORTER_ASSERT(reporter, bounds.contains(verts->bounds()));
150 }
Jim Van Vertha8682202020-12-17 10:18:16 -0500151
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 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400170}
171
172void check_bounds(skiatest::Reporter* reporter, const SkPath& path) {
Mike Reedbb59dfa2019-12-12 14:48:28 -0500173 const bool fixed_shadows_in_perspective = false; // skbug.com/9698
174
Jim Van Verth1af03d42017-07-31 09:34:58 -0400175 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);
Mike Reedbb59dfa2019-12-12 14:48:28 -0500184 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 }
Jim Van Verth1af03d42017-07-31 09:34:58 -0400195}
196
197DEF_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}