• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 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/SkPath.h"
9 #include "src/core/SkRasterClip.h"
10 #include "src/core/SkRegionPriv.h"
11 
SkRasterClip(const SkRasterClip & that)12 SkRasterClip::SkRasterClip(const SkRasterClip& that)
13         : fIsBW(that.fIsBW)
14         , fIsEmpty(that.fIsEmpty)
15         , fIsRect(that.fIsRect)
16         , fShader(that.fShader)
17 {
18     AUTO_RASTERCLIP_VALIDATE(that);
19 
20     if (fIsBW) {
21         fBW = that.fBW;
22     } else {
23         fAA = that.fAA;
24     }
25 
26     SkDEBUGCODE(this->validate();)
27 }
28 
operator =(const SkRasterClip & that)29 SkRasterClip& SkRasterClip::operator=(const SkRasterClip& that) {
30     AUTO_RASTERCLIP_VALIDATE(that);
31 
32     fIsBW = that.fIsBW;
33     if (fIsBW) {
34         fBW = that.fBW;
35     } else {
36         fAA = that.fAA;
37     }
38 
39     fIsEmpty = that.isEmpty();
40     fIsRect = that.isRect();
41     fShader = that.fShader;
42     SkDEBUGCODE(this->validate();)
43     return *this;
44 }
45 
SkRasterClip(const SkRegion & rgn)46 SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
47     fIsBW = true;
48     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
49     fIsRect = !fIsEmpty;
50     SkDEBUGCODE(this->validate();)
51 }
52 
SkRasterClip(const SkIRect & bounds)53 SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
54     fIsBW = true;
55     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
56     fIsRect = !fIsEmpty;
57     SkDEBUGCODE(this->validate();)
58 }
59 
SkRasterClip()60 SkRasterClip::SkRasterClip() {
61     fIsBW = true;
62     fIsEmpty = true;
63     fIsRect = false;
64     SkDEBUGCODE(this->validate();)
65 }
66 
SkRasterClip(const SkPath & path,const SkIRect & bounds,bool doAA)67 SkRasterClip::SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA) {
68     if (doAA) {
69         fIsBW = false;
70         fAA.setPath(path, bounds, true);
71     } else {
72         fIsBW = true;
73         fBW.setPath(path, SkRegion(bounds));
74     }
75     fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
76     fIsRect = this->computeIsRect();
77     SkDEBUGCODE(this->validate();)
78 }
79 
~SkRasterClip()80 SkRasterClip::~SkRasterClip() {
81     SkDEBUGCODE(this->validate();)
82 }
83 
setEmpty()84 bool SkRasterClip::setEmpty() {
85     AUTO_RASTERCLIP_VALIDATE(*this);
86 
87     fIsBW = true;
88     fBW.setEmpty();
89     fAA.setEmpty();
90     fIsEmpty = true;
91     fIsRect = false;
92     return false;
93 }
94 
setRect(const SkIRect & rect)95 bool SkRasterClip::setRect(const SkIRect& rect) {
96     AUTO_RASTERCLIP_VALIDATE(*this);
97 
98     fIsBW = true;
99     fAA.setEmpty();
100     fIsRect = fBW.setRect(rect);
101     fIsEmpty = !fIsRect;
102     return fIsRect;
103 }
104 
105 /////////////////////////////////////////////////////////////////////////////////////
106 
op(const SkIRect & rect,SkClipOp op)107 bool SkRasterClip::op(const SkIRect& rect, SkClipOp op) {
108     AUTO_RASTERCLIP_VALIDATE(*this);
109 
110     if (fIsBW) {
111         fBW.op(rect, (SkRegion::Op) op);
112     } else {
113         fAA.op(rect, op);
114     }
115     return this->updateCacheAndReturnNonEmpty();
116 }
117 
op(const SkRegion & rgn,SkClipOp op)118 bool SkRasterClip::op(const SkRegion& rgn, SkClipOp op) {
119     AUTO_RASTERCLIP_VALIDATE(*this);
120 
121     if (fIsBW) {
122         (void)fBW.op(rgn, (SkRegion::Op) op);
123     } else {
124         SkAAClip tmp;
125         tmp.setRegion(rgn);
126         (void)fAA.op(tmp, op);
127     }
128     return this->updateCacheAndReturnNonEmpty();
129 }
130 
131 /**
132  *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
133  *  axis. Thus we can treat an axis coordinate as an integer if it differs
134  *  from its nearest int by < half of that value (1/8 in this case).
135  */
nearly_integral(SkScalar x)136 static bool nearly_integral(SkScalar x) {
137     static const SkScalar domain = SK_Scalar1 / 4;
138     static const SkScalar halfDomain = domain / 2;
139 
140     x += halfDomain;
141     return x - SkScalarFloorToScalar(x) < domain;
142 }
143 
op(const SkRect & localRect,const SkMatrix & matrix,SkClipOp op,bool doAA)144 bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
145     AUTO_RASTERCLIP_VALIDATE(*this);
146 
147     const bool isScaleTrans = matrix.isScaleTranslate();
148     if (!isScaleTrans) {
149         return this->op(SkPath::Rect(localRect), matrix, op, doAA);
150     }
151 
152     SkRect devRect = matrix.mapRect(localRect);
153     if (fIsBW && doAA) {
154         // check that the rect really needs aa, or is it close enought to
155         // integer boundaries that we can just treat it as a BW rect?
156         if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
157             nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
158             doAA = false;
159         }
160     }
161 
162     if (fIsBW && !doAA) {
163         (void)fBW.op(devRect.round(), (SkRegion::Op) op);
164     } else {
165         if (fIsBW) {
166             this->convertToAA();
167         }
168         (void)fAA.op(devRect, op, doAA);
169     }
170     return this->updateCacheAndReturnNonEmpty();
171 }
172 
op(const SkRRect & rrect,const SkMatrix & matrix,SkClipOp op,bool doAA)173 bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
174     return this->op(SkPath::RRect(rrect), matrix, op, doAA);
175 }
176 
op(const SkPath & path,const SkMatrix & matrix,SkClipOp op,bool doAA)177 bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, bool doAA) {
178     AUTO_RASTERCLIP_VALIDATE(*this);
179 
180     SkPath devPath;
181     path.transform(matrix, &devPath);
182 
183     // Since op is either intersect or difference, the clip is always shrinking; that means we can
184     // always use our current bounds as the limiting factor for region/aaclip operations.
185     if (this->isRect() && op == SkClipOp::kIntersect) {
186         // However, in the relatively common case of intersecting a new path with a rectangular
187         // clip, it's faster to convert the path into a region/aa-mask in place than evaluate the
188         // actual intersection. See skbug.com/12398
189         if (doAA && fIsBW) {
190             this->convertToAA();
191         }
192         if (fIsBW) {
193             fBW.setPath(devPath, SkRegion(this->getBounds()));
194         } else {
195             fAA.setPath(devPath, this->getBounds(), doAA);
196         }
197         return this->updateCacheAndReturnNonEmpty();
198     } else {
199         return this->op(SkRasterClip(devPath, this->getBounds(), doAA), op);
200     }
201 }
202 
op(sk_sp<SkShader> sh)203 bool SkRasterClip::op(sk_sp<SkShader> sh) {
204     AUTO_RASTERCLIP_VALIDATE(*this);
205 
206     if (!fShader) {
207         fShader = sh;
208     } else {
209         fShader = SkShaders::Blend(SkBlendMode::kSrcIn, sh, fShader);
210     }
211     return !this->isEmpty();
212 }
213 
op(const SkRasterClip & clip,SkClipOp op)214 bool SkRasterClip::op(const SkRasterClip& clip, SkClipOp op) {
215     AUTO_RASTERCLIP_VALIDATE(*this);
216     clip.validate();
217 
218     if (this->isBW() && clip.isBW()) {
219         (void)fBW.op(clip.fBW, (SkRegion::Op) op);
220     } else {
221         SkAAClip tmp;
222         const SkAAClip* other;
223 
224         if (this->isBW()) {
225             this->convertToAA();
226         }
227         if (clip.isBW()) {
228             tmp.setRegion(clip.bwRgn());
229             other = &tmp;
230         } else {
231             other = &clip.aaRgn();
232         }
233         (void)fAA.op(*other, op);
234     }
235     return this->updateCacheAndReturnNonEmpty();
236 }
237 
translate(int dx,int dy,SkRasterClip * dst) const238 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
239     if (nullptr == dst) {
240         return;
241     }
242 
243     AUTO_RASTERCLIP_VALIDATE(*this);
244 
245     if (this->isEmpty()) {
246         dst->setEmpty();
247         return;
248     }
249     if (0 == (dx | dy)) {
250         *dst = *this;
251         return;
252     }
253 
254     dst->fIsBW = fIsBW;
255     if (fIsBW) {
256         fBW.translate(dx, dy, &dst->fBW);
257         dst->fAA.setEmpty();
258     } else {
259         fAA.translate(dx, dy, &dst->fAA);
260         dst->fBW.setEmpty();
261     }
262     dst->updateCacheAndReturnNonEmpty();
263 }
264 
convertToAA()265 void SkRasterClip::convertToAA() {
266     AUTO_RASTERCLIP_VALIDATE(*this);
267 
268     SkASSERT(fIsBW);
269     fAA.setRegion(fBW);
270     fIsBW = false;
271 
272     // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
273     // ourselves back to BW.
274     (void)this->updateCacheAndReturnNonEmpty(false);
275 }
276 
277 #ifdef SK_DEBUG
validate() const278 void SkRasterClip::validate() const {
279     // can't ever assert that fBW is empty, since we may have called forceGetBW
280     if (fIsBW) {
281         SkASSERT(fAA.isEmpty());
282     }
283 
284     SkRegionPriv::Validate(fBW);
285     fAA.validate();
286 
287     SkASSERT(this->computeIsEmpty() == fIsEmpty);
288     SkASSERT(this->computeIsRect() == fIsRect);
289 }
290 #endif
291 
292 ///////////////////////////////////////////////////////////////////////////////
293 
SkAAClipBlitterWrapper()294 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
295     SkDEBUGCODE(fClipRgn = nullptr;)
296     SkDEBUGCODE(fBlitter = nullptr;)
297 }
298 
SkAAClipBlitterWrapper(const SkRasterClip & clip,SkBlitter * blitter)299 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
300                                                SkBlitter* blitter) {
301     this->init(clip, blitter);
302 }
303 
SkAAClipBlitterWrapper(const SkAAClip * aaclip,SkBlitter * blitter)304 SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
305                                                SkBlitter* blitter) {
306     SkASSERT(blitter);
307     SkASSERT(aaclip);
308     fBWRgn.setRect(aaclip->getBounds());
309     fAABlitter.init(blitter, aaclip);
310     // now our return values
311     fClipRgn = &fBWRgn;
312     fBlitter = &fAABlitter;
313 }
314 
init(const SkRasterClip & clip,SkBlitter * blitter)315 void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
316     SkASSERT(blitter);
317     if (clip.isBW()) {
318         fClipRgn = &clip.bwRgn();
319         fBlitter = blitter;
320     } else {
321         const SkAAClip& aaclip = clip.aaRgn();
322         fBWRgn.setRect(aaclip.getBounds());
323         fAABlitter.init(blitter, &aaclip);
324         // now our return values
325         fClipRgn = &fBWRgn;
326         fBlitter = &fAABlitter;
327     }
328 }
329