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