1 /*
2 * Copyright 2018 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 "include/effects/SkOpPathEffect.h"
9
10 #include "include/core/SkFlattenable.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkPathEffect.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkScalar.h"
18 #include "include/core/SkStrokeRec.h"
19 #include "include/core/SkTypes.h"
20 #include "include/pathops/SkPathOps.h"
21 #include "include/private/base/SkPathEnums.h"
22 #include "include/private/base/SkTo.h"
23 #include "src/core/SkPathEffectBase.h"
24 #include "src/core/SkReadBuffer.h"
25 #include "src/core/SkWriteBuffer.h"
26 #include "src/effects/SkOpPE.h"
27
28 #include <utility>
29
Make(sk_sp<SkPathEffect> one,sk_sp<SkPathEffect> two,SkPathOp op)30 sk_sp<SkPathEffect> SkMergePathEffect::Make(sk_sp<SkPathEffect> one, sk_sp<SkPathEffect> two,
31 SkPathOp op) {
32 return sk_sp<SkPathEffect>(new SkOpPE(std::move(one), std::move(two), op));
33 }
34
SkOpPE(sk_sp<SkPathEffect> one,sk_sp<SkPathEffect> two,SkPathOp op)35 SkOpPE::SkOpPE(sk_sp<SkPathEffect> one, sk_sp<SkPathEffect> two, SkPathOp op)
36 : fOne(std::move(one)), fTwo(std::move(two)), fOp(op) {}
37
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect * cull,const SkMatrix & ctm) const38 bool SkOpPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
39 const SkRect* cull, const SkMatrix& ctm) const {
40 SkPath one, two;
41 if (fOne) {
42 if (!fOne->filterPath(&one, src, rec, cull, ctm)) {
43 return false;
44 }
45 } else {
46 one = src;
47 }
48 if (fTwo) {
49 if (!fTwo->filterPath(&two, src, rec, cull, ctm)) {
50 return false;
51 }
52 } else {
53 two = src;
54 }
55 return Op(one, two, fOp, dst);
56 }
57
computeFastBounds(SkRect * bounds) const58 bool SkOpPE::computeFastBounds(SkRect* bounds) const {
59 if (!bounds) {
60 return (!SkToBool(fOne) || as_PEB(fOne)->computeFastBounds(nullptr)) &&
61 (!SkToBool(fTwo) || as_PEB(fTwo)->computeFastBounds(nullptr));
62 }
63
64 // bounds will hold the result of the fOne while b2 holds the result of fTwo's fast bounds
65 SkRect b2 = *bounds;
66 if (fOne && !as_PEB(fOne)->computeFastBounds(bounds)) {
67 return false;
68 }
69 if (fTwo && !as_PEB(fTwo)->computeFastBounds(&b2)) {
70 return false;
71 }
72
73 switch (fOp) {
74 case SkPathOp::kIntersect_SkPathOp:
75 if (!bounds->intersect(b2)) {
76 bounds->setEmpty();
77 }
78 break;
79 case SkPathOp::kDifference_SkPathOp:
80 // (one - two) conservatively leaves one's bounds unmodified
81 break;
82 case SkPathOp::kReverseDifference_SkPathOp:
83 // (two - one) conservatively leaves two's bounds unmodified
84 *bounds = b2;
85 break;
86 case SkPathOp::kXOR_SkPathOp:
87 // fall through to union since XOR computes a subset of regular OR
88 case SkPathOp::kUnion_SkPathOp:
89 bounds->join(b2);
90 break;
91 }
92
93 return true;
94 }
95
flatten(SkWriteBuffer & buffer) const96 void SkOpPE::flatten(SkWriteBuffer& buffer) const {
97 buffer.writeFlattenable(fOne.get());
98 buffer.writeFlattenable(fTwo.get());
99 buffer.write32(fOp);
100 }
101
CreateProc(SkReadBuffer & buffer)102 sk_sp<SkFlattenable> SkOpPE::CreateProc(SkReadBuffer& buffer) {
103 auto one = buffer.readPathEffect();
104 auto two = buffer.readPathEffect();
105 SkPathOp op = buffer.read32LE(kReverseDifference_SkPathOp);
106 return buffer.isValid() ? SkMergePathEffect::Make(std::move(one), std::move(two), op) : nullptr;
107 }
108
109 //////////////////////////////////////////////////////////////////////////////////////////////////
110
MakeTranslate(SkScalar dx,SkScalar dy)111 sk_sp<SkPathEffect> SkMatrixPathEffect::MakeTranslate(SkScalar dx, SkScalar dy) {
112 if (!SkScalarsAreFinite(dx, dy)) {
113 return nullptr;
114 }
115 return sk_sp<SkPathEffect>(new SkMatrixPE(SkMatrix::Translate(dx, dy)));
116 }
117
Make(const SkMatrix & matrix)118 sk_sp<SkPathEffect> SkMatrixPathEffect::Make(const SkMatrix& matrix) {
119 if (!matrix.isFinite()) {
120 return nullptr;
121 }
122 return sk_sp<SkPathEffect>(new SkMatrixPE(matrix));
123 }
124
SkMatrixPE(const SkMatrix & matrix)125 SkMatrixPE::SkMatrixPE(const SkMatrix& matrix) : fMatrix(matrix) {
126 SkASSERT(matrix.isFinite());
127 }
128
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec *,const SkRect *,const SkMatrix &) const129 bool SkMatrixPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
130 const SkMatrix&) const {
131 src.transform(fMatrix, dst);
132 return true;
133 }
134
flatten(SkWriteBuffer & buffer) const135 void SkMatrixPE::flatten(SkWriteBuffer& buffer) const {
136 buffer.writeMatrix(fMatrix);
137 }
138
CreateProc(SkReadBuffer & buffer)139 sk_sp<SkFlattenable> SkMatrixPE::CreateProc(SkReadBuffer& buffer) {
140 SkMatrix mx;
141 buffer.readMatrix(&mx);
142 return buffer.isValid() ? SkMatrixPathEffect::Make(mx) : nullptr;
143 }
144
145 //////////////////////////////////////////////////////////////////////////////////////////////////
146
Make(SkScalar width,SkPaint::Join join,SkPaint::Cap cap,SkScalar miter)147 sk_sp<SkPathEffect> SkStrokePathEffect::Make(SkScalar width, SkPaint::Join join, SkPaint::Cap cap,
148 SkScalar miter) {
149 if (!SkScalarsAreFinite(width, miter) || width < 0 || miter < 0) {
150 return nullptr;
151 }
152 return sk_sp<SkPathEffect>(new SkStrokePE(width, join, cap, miter));
153 }
154
SkStrokePE(SkScalar width,SkPaint::Join join,SkPaint::Cap cap,SkScalar miter)155 SkStrokePE::SkStrokePE(SkScalar width, SkPaint::Join join, SkPaint::Cap cap, SkScalar miter)
156 : fWidth(width), fMiter(miter), fJoin(join), fCap(cap) {}
157
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec *,const SkRect *,const SkMatrix &) const158 bool SkStrokePE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
159 const SkMatrix&) const {
160 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
161 rec.setStrokeStyle(fWidth);
162 rec.setStrokeParams(fCap, fJoin, fMiter);
163 return rec.applyToPath(dst, src);
164 }
165
computeFastBounds(SkRect * bounds) const166 bool SkStrokePE::computeFastBounds(SkRect* bounds) const {
167 if (bounds) {
168 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
169 rec.setStrokeStyle(fWidth);
170 rec.setStrokeParams(fCap, fJoin, fMiter);
171 bounds->outset(rec.getInflationRadius(), rec.getInflationRadius());
172 }
173 return true;
174 }
175
flatten(SkWriteBuffer & buffer) const176 void SkStrokePE::flatten(SkWriteBuffer& buffer) const {
177 buffer.writeScalar(fWidth);
178 buffer.writeScalar(fMiter);
179 buffer.write32(fJoin);
180 buffer.write32(fCap);
181 }
182
CreateProc(SkReadBuffer & buffer)183 sk_sp<SkFlattenable> SkStrokePE::CreateProc(SkReadBuffer& buffer) {
184 SkScalar width = buffer.readScalar();
185 SkScalar miter = buffer.readScalar();
186 SkPaint::Join join = buffer.read32LE(SkPaint::kLast_Join);
187 SkPaint::Cap cap = buffer.read32LE(SkPaint::kLast_Cap);
188 return buffer.isValid() ? SkStrokePathEffect::Make(width, join, cap, miter) : nullptr;
189 }
190
191 //////////////////////////////////////////////////////////////////////////////////////////////////
192
193 #include "include/effects/SkStrokeAndFillPathEffect.h"
194 #include "src/core/SkPathPriv.h"
195
Make()196 sk_sp<SkPathEffect> SkStrokeAndFillPathEffect::Make() {
197 static SkPathEffect* strokeAndFill = new SkStrokeAndFillPE;
198 return sk_ref_sp(strokeAndFill);
199 }
200
flatten(SkWriteBuffer &) const201 void SkStrokeAndFillPE::flatten(SkWriteBuffer&) const {}
202
known_to_be_opposite_directions(const SkPath & a,const SkPath & b)203 static bool known_to_be_opposite_directions(const SkPath& a, const SkPath& b) {
204 auto a_dir = SkPathPriv::ComputeFirstDirection(a),
205 b_dir = SkPathPriv::ComputeFirstDirection(b);
206
207 return (a_dir == SkPathFirstDirection::kCCW &&
208 b_dir == SkPathFirstDirection::kCW)
209 ||
210 (a_dir == SkPathFirstDirection::kCW &&
211 b_dir == SkPathFirstDirection::kCCW);
212 }
213
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec * rec,const SkRect *,const SkMatrix &) const214 bool SkStrokeAndFillPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
215 const SkRect*, const SkMatrix&) const {
216 // This one is weird, since we exist to allow this paint-style to go away. If we see it,
217 // just let the normal machine run its course.
218 if (rec->getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
219 *dst = src;
220 return true;
221 }
222
223 if (rec->getStyle() == SkStrokeRec::kStroke_Style) {
224 if (!rec->applyToPath(dst, src)) {
225 return false;
226 }
227
228 if (known_to_be_opposite_directions(src, *dst)) {
229 dst->reverseAddPath(src);
230 } else {
231 dst->addPath(src);
232 }
233 } else {
234 *dst = src;
235 }
236 rec->setFillStyle();
237 return true;
238 }
239
CreateProc(SkReadBuffer & buffer)240 sk_sp<SkFlattenable> SkStrokeAndFillPE::CreateProc(SkReadBuffer& buffer) {
241 return SkStrokeAndFillPathEffect::Make();
242 }
243