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