1 /*
2 * Copyright 2017 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/SkSGMerge.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkClipOp.h"
12 #include "include/core/SkPoint.h"
13 #include "include/pathops/SkPathOps.h"
14 #include "include/private/base/SkAssert.h"
15 #include "modules/sksg/include/SkSGNode.h"
16 #include "src/core/SkPathPriv.h"
17
18 class SkMatrix;
19
20 namespace sksg {
21
Merge(std::vector<Rec> && recs)22 Merge::Merge(std::vector<Rec>&& recs)
23 : fRecs(std::move(recs)) {
24 for (const auto& rec : fRecs) {
25 this->observeInval(rec.fGeo);
26 }
27 }
28
~Merge()29 Merge::~Merge() {
30 for (const auto& rec : fRecs) {
31 this->unobserveInval(rec.fGeo);
32 }
33 }
34
onClip(SkCanvas * canvas,bool antiAlias) const35 void Merge::onClip(SkCanvas* canvas, bool antiAlias) const {
36 canvas->clipPath(fMerged, SkClipOp::kIntersect, antiAlias);
37 }
38
onDraw(SkCanvas * canvas,const SkPaint & paint) const39 void Merge::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
40 canvas->drawPath(fMerged, paint);
41 }
42
onContains(const SkPoint & p) const43 bool Merge::onContains(const SkPoint& p) const {
44 return fMerged.contains(p.x(), p.y());
45 }
46
onAsPath() const47 SkPath Merge::onAsPath() const {
48 return fMerged;
49 }
50
mode_to_op(Merge::Mode mode)51 static SkPathOp mode_to_op(Merge::Mode mode) {
52 switch (mode) {
53 case Merge::Mode::kUnion:
54 return kUnion_SkPathOp;
55 case Merge::Mode::kIntersect:
56 return kIntersect_SkPathOp;
57 case Merge::Mode::kDifference:
58 return kDifference_SkPathOp;
59 case Merge::Mode::kReverseDifference:
60 return kReverseDifference_SkPathOp;
61 case Merge::Mode::kXOR:
62 return kXOR_SkPathOp;
63 default:
64 break;
65 }
66
67 return kUnion_SkPathOp;
68 }
69
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)70 SkRect Merge::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
71 SkASSERT(this->hasInval());
72
73 SkOpBuilder builder;
74
75 fMerged.reset();
76 bool in_builder = false;
77
78 auto append = [&](const SkPath& path) {
79 if (in_builder) {
80 builder.resolve(&fMerged);
81 in_builder = false;
82 }
83
84 if (fMerged.isEmpty()) {
85 // First merge path determines the fill type.
86 fMerged = path;
87 } else {
88 fMerged.addPath(path);
89 }
90 };
91
92 for (const auto& rec : fRecs) {
93 rec.fGeo->revalidate(ic, ctm);
94
95 if (rec.fMode == Mode::kMerge) {
96 // Merge (append) is not supported by SkOpBuidler.
97 append(rec.fGeo->asPath());
98 continue;
99 }
100
101 if (!in_builder) {
102 builder.add(fMerged, kUnion_SkPathOp);
103 in_builder = true;
104 }
105
106 builder.add(rec.fGeo->asPath(), mode_to_op(rec.fMode));
107 }
108
109 if (in_builder) {
110 builder.resolve(&fMerged);
111 }
112
113 SkPathPriv::ShrinkToFit(&fMerged);
114
115 return fMerged.computeTightBounds();
116 }
117
118 } // namespace sksg
119