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