1/*
2 * Copyright 2020 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#ifndef SkSGGeometryEffect_DEFINED
9#define SkSGGeometryEffect_DEFINED
10
11#include "modules/sksg/include/SkSGGeometryNode.h"
12
13#include "include/core/SkPaint.h"
14#include "include/core/SkPath.h"
15#include "include/effects/SkTrimPathEffect.h"
16#include "modules/sksg/include/SkSGTransform.h"
17
18namespace sksg {
19
20/**
21 * Base class for geometry effects.
22 */
23class GeometryEffect : public GeometryNode {
24protected:
25 explicit GeometryEffect(sk_sp<GeometryNode>);
26 ~GeometryEffect() override;
27
28 void onClip(SkCanvas*, bool antiAlias) const final;
29 void onDraw(SkCanvas*, const SkPaint&) const final;
30 bool onContains(const SkPoint&) const final;
31
32 SkRect onRevalidate(InvalidationController*, const SkMatrix&) final;
33 SkPath onAsPath() const final;
34
35 virtual SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) = 0;
36
37private:
38 const sk_sp<GeometryNode> fChild;
39 SkPath fPath; // transformed child cache.
40
41 using INHERITED = GeometryNode;
42};
43
44/**
45 * Apply a trim effect to the child geometry.
46 */
47class TrimEffect final : public GeometryEffect {
48public:
49 static sk_sp<TrimEffect> Make(sk_sp<GeometryNode> child) {
50 return child ? sk_sp<TrimEffect>(new TrimEffect(std::move(child))) : nullptr;
51 }
52
53 SG_ATTRIBUTE(Start , SkScalar , fStart )
54 SG_ATTRIBUTE(Stop , SkScalar , fStop )
55 SG_ATTRIBUTE(Mode , SkTrimPathEffect::Mode, fMode )
56
57private:
58 explicit TrimEffect(sk_sp<GeometryNode> child) : INHERITED(std::move(child)) {}
59
60 SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) override;
61
62 SkScalar fStart = 0,
63 fStop = 1;
64 SkTrimPathEffect::Mode fMode = SkTrimPathEffect::Mode::kNormal;
65
66 using INHERITED = GeometryEffect;
67};
68
69/**
70 * Apply a transform to a GeometryNode.
71 */
72class GeometryTransform final : public GeometryEffect {
73public:
74 static sk_sp<GeometryTransform> Make(sk_sp<GeometryNode> child, sk_sp<Transform> transform) {
75 return child && transform
76 ? sk_sp<GeometryTransform>(new GeometryTransform(std::move(child),
77 std::move(transform)))
78 : nullptr;
79 }
80
81 ~GeometryTransform() override;
82
83 const sk_sp<Transform>& getTransform() const { return fTransform; }
84
85private:
86 GeometryTransform(sk_sp<GeometryNode>, sk_sp<Transform>);
87
88 SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) override;
89
90 const sk_sp<Transform> fTransform;
91
92 using INHERITED = GeometryEffect;
93};
94
95/**
96 * Apply a dash effect to the child geometry.
97 *
98 * Follows the same semantics as SkDashPathEffect, with one minor tweak: when the number of
99 * intervals is odd, they are repeated once more to attain an even sequence (same as SVG
100 * stroke-dasharray: https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty).
101 */
102class DashEffect final : public GeometryEffect {
103public:
104 static sk_sp<DashEffect> Make(sk_sp<GeometryNode> child) {
105 return child ? sk_sp<DashEffect>(new DashEffect(std::move(child))) : nullptr;
106 }
107
108 SG_ATTRIBUTE(Intervals, std::vector<float>, fIntervals)
109 SG_ATTRIBUTE(Phase, float , fPhase )
110
111private:
112 explicit DashEffect(sk_sp<GeometryNode> child) : INHERITED(std::move(child)) {}
113
114 SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) override;
115
116 std::vector<float> fIntervals;
117 float fPhase;
118
119 using INHERITED = GeometryEffect;
120};
121
122/**
123 * Apply a rounded-corner effect to the child geometry.
124 */
125class RoundEffect final : public GeometryEffect {
126public:
127 static sk_sp<RoundEffect> Make(sk_sp<GeometryNode> child) {
128 return child ? sk_sp<RoundEffect>(new RoundEffect(std::move(child))) : nullptr;
129 }
130
131 SG_ATTRIBUTE(Radius, SkScalar, fRadius)
132
133private:
134 explicit RoundEffect(sk_sp<GeometryNode> child) : INHERITED(std::move(child)) {}
135
136 SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) override;
137
138 SkScalar fRadius = 0;
139
140 using INHERITED = GeometryEffect;
141};
142
143/**
144 * Apply an offset effect to the child geometry.
145 */
146class OffsetEffect final : public GeometryEffect {
147public:
148 static sk_sp<OffsetEffect> Make(sk_sp<GeometryNode> child) {
149 return child ? sk_sp<OffsetEffect>(new OffsetEffect(std::move(child))) : nullptr;
150 }
151
152 SG_ATTRIBUTE(Offset , SkScalar , fOffset )
153 SG_ATTRIBUTE(MiterLimit , SkScalar , fMiterLimit)
154 SG_ATTRIBUTE(Join , SkPaint::Join, fJoin )
155
156private:
157 explicit OffsetEffect(sk_sp<GeometryNode> child) : INHERITED(std::move(child)) {}
158
159 SkPath onRevalidateEffect(const sk_sp<GeometryNode>&) override;
160
161 SkScalar fOffset = 0,
162 fMiterLimit = 4;
163 SkPaint::Join fJoin = SkPaint::kMiter_Join;
164
165 using INHERITED = GeometryEffect;
166};
167
168} // namespace sksg
169
170#endif // SkSGGeometryEffect_DEFINED
171