• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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