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