• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "modules/sksg/include/SkSGGeometryEffect.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkStrokeRec.h"
12 #include "include/effects/SkCornerPathEffect.h"
13 #include "include/effects/SkDashPathEffect.h"
14 #include "include/effects/SkTrimPathEffect.h"
15 #include "include/pathops/SkPathOps.h"
16 #include "modules/sksg/src/SkSGTransformPriv.h"
17 #include "src/core/SkPathEffectBase.h"
18 #include "src/core/SkPathPriv.h"
19 
20 #include <cmath>
21 
22 namespace sksg {
23 
GeometryEffect(sk_sp<GeometryNode> child)24 GeometryEffect::GeometryEffect(sk_sp<GeometryNode> child)
25     : fChild(std::move(child)) {
26     SkASSERT(fChild);
27 
28     this->observeInval(fChild);
29 }
30 
~GeometryEffect()31 GeometryEffect::~GeometryEffect() {
32     this->unobserveInval(fChild);
33 }
34 
onClip(SkCanvas * canvas,bool antiAlias) const35 void GeometryEffect::onClip(SkCanvas* canvas, bool antiAlias) const {
36     canvas->clipPath(fPath, SkClipOp::kIntersect, antiAlias);
37 }
38 
onDraw(SkCanvas * canvas,const SkPaint & paint) const39 void GeometryEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
40     canvas->drawPath(fPath, paint);
41 }
42 
onContains(const SkPoint & p) const43 bool GeometryEffect::onContains(const SkPoint& p) const {
44     return fPath.contains(p.x(), p.y());
45 }
46 
onAsPath() const47 SkPath GeometryEffect::onAsPath() const {
48     return fPath;
49 }
50 
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)51 SkRect GeometryEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
52     SkASSERT(this->hasInval());
53 
54     fChild->revalidate(ic, ctm);
55 
56     fPath = this->onRevalidateEffect(fChild);
57     SkPathPriv::ShrinkToFit(&fPath);
58 
59     return fPath.computeTightBounds();
60 }
61 
onRevalidateEffect(const sk_sp<GeometryNode> & child)62 SkPath TrimEffect::onRevalidateEffect(const sk_sp<GeometryNode>& child) {
63     SkPath path = child->asPath();
64 
65     if (const auto trim = SkTrimPathEffect::Make(fStart, fStop, fMode)) {
66         SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
67         SkASSERT(!trim->needsCTM());
68         SkAssertResult(trim->filterPath(&path, path, &rec, nullptr));
69     }
70 
71     return path;
72 }
73 
GeometryTransform(sk_sp<GeometryNode> child,sk_sp<Transform> transform)74 GeometryTransform::GeometryTransform(sk_sp<GeometryNode> child, sk_sp<Transform> transform)
75     : INHERITED(std::move(child))
76     , fTransform(std::move(transform)) {
77     SkASSERT(fTransform);
78     this->observeInval(fTransform);
79 }
80 
~GeometryTransform()81 GeometryTransform::~GeometryTransform() {
82     this->unobserveInval(fTransform);
83 }
84 
onRevalidateEffect(const sk_sp<GeometryNode> & child)85 SkPath GeometryTransform::onRevalidateEffect(const sk_sp<GeometryNode>& child) {
86     fTransform->revalidate(nullptr, SkMatrix::I());
87     const auto m = TransformPriv::As<SkMatrix>(fTransform);
88 
89     SkPath path = child->asPath();
90     path.transform(m);
91 
92     return path;
93 }
94 
95 namespace  {
96 
make_dash(const std::vector<float> intervals,float phase)97 sk_sp<SkPathEffect> make_dash(const std::vector<float> intervals, float phase) {
98     if (intervals.empty()) {
99         return nullptr;
100     }
101 
102     const auto* intervals_ptr   = intervals.data();
103     auto        intervals_count = intervals.size();
104 
105     SkSTArray<32, float, true> storage;
106     if (intervals_count & 1) {
107         intervals_count *= 2;
108         storage.resize(intervals_count);
109         intervals_ptr = storage.data();
110 
111         std::copy(intervals.begin(), intervals.end(), storage.begin());
112         std::copy(intervals.begin(), intervals.end(), storage.begin() + intervals.size());
113     }
114 
115     return SkDashPathEffect::Make(intervals_ptr, SkToInt(intervals_count), phase);
116 }
117 
118 } // namespace
119 
onRevalidateEffect(const sk_sp<GeometryNode> & child)120 SkPath DashEffect::onRevalidateEffect(const sk_sp<GeometryNode>& child) {
121     SkPath path = child->asPath();
122 
123     if (const auto dash_patheffect = make_dash(fIntervals, fPhase)) {
124         SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
125         SkASSERT(!dash_patheffect->needsCTM());
126         dash_patheffect->filterPath(&path, path, &rec, nullptr);
127     }
128 
129     return path;
130 }
131 
onRevalidateEffect(const sk_sp<GeometryNode> & child)132 SkPath RoundEffect::onRevalidateEffect(const sk_sp<GeometryNode>& child) {
133     SkPath path = child->asPath();
134 
135     if (const auto round = SkCornerPathEffect::Make(fRadius)) {
136         SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
137         SkASSERT(!round->needsCTM());
138         SkAssertResult(round->filterPath(&path, path, &rec, nullptr));
139     }
140 
141     return path;
142 }
143 
onRevalidateEffect(const sk_sp<GeometryNode> & child)144 SkPath OffsetEffect::onRevalidateEffect(const sk_sp<GeometryNode>& child) {
145     SkPath path = child->asPath();
146 
147     if (!SkScalarNearlyZero(fOffset)) {
148         SkPaint paint;
149         paint.setStyle(SkPaint::kStroke_Style);
150         paint.setStrokeWidth(std::abs(fOffset) * 2);
151         paint.setStrokeMiter(fMiterLimit);
152         paint.setStrokeJoin(fJoin);
153 
154         SkPath fill_path;
155         paint.getFillPath(path, &fill_path, nullptr);
156 
157         if (fOffset > 0) {
158             Op(path, fill_path, kUnion_SkPathOp, &path);
159         } else {
160             Op(path, fill_path, kDifference_SkPathOp, &path);
161         }
162 
163         // TODO: this seems to break path combining (winding mismatch?)
164         // Simplify(path, &path);
165     }
166 
167     return path;
168 }
169 
170 }  // namespace sksg
171