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