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