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