blob: dab91d8f4ce31434af80839208961509e5746761 [file] [log] [blame]
reed@google.comd1e3c5f2011-10-10 19:36:25 +00001/*
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 "SkRasterClip.h"
9
10
11SkRasterClip::SkRasterClip() {
12 fIsBW = true;
reed@google.coma1c6ff42012-05-11 14:36:57 +000013 fIsEmpty = true;
14 fIsRect = false;
15 SkDEBUGCODE(this->validate();)
reed@google.comd1e3c5f2011-10-10 19:36:25 +000016}
17
reed@google.com34f7e472011-10-13 15:11:59 +000018SkRasterClip::SkRasterClip(const SkRasterClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +000019 AUTO_RASTERCLIP_VALIDATE(src);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000020
reed@google.com34f7e472011-10-13 15:11:59 +000021 fIsBW = src.fIsBW;
22 if (fIsBW) {
23 fBW = src.fBW;
24 } else {
25 fAA = src.fAA;
26 }
reed@google.coma1c6ff42012-05-11 14:36:57 +000027
28 fIsEmpty = src.isEmpty();
29 fIsRect = src.isRect();
30 SkDEBUGCODE(this->validate();)
reed@google.com34f7e472011-10-13 15:11:59 +000031}
32
reed@google.comba16da92011-10-11 13:15:03 +000033SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
34 fIsBW = true;
reed@google.coma1c6ff42012-05-11 14:36:57 +000035 fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
36 fIsRect = !fIsEmpty;
37 SkDEBUGCODE(this->validate();)
reed@google.comba16da92011-10-11 13:15:03 +000038}
39
reed@google.com045e62d2011-10-24 12:19:46 +000040SkRasterClip::~SkRasterClip() {
reed@google.coma1c6ff42012-05-11 14:36:57 +000041 SkDEBUGCODE(this->validate();)
reed@google.comd1e3c5f2011-10-10 19:36:25 +000042}
43
44bool SkRasterClip::isComplex() const {
45 return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
46}
47
48const SkIRect& SkRasterClip::getBounds() const {
49 return fIsBW ? fBW.getBounds() : fAA.getBounds();
50}
51
52bool SkRasterClip::setEmpty() {
reed@google.com045e62d2011-10-24 12:19:46 +000053 AUTO_RASTERCLIP_VALIDATE(*this);
54
reed@google.comd1e3c5f2011-10-10 19:36:25 +000055 fIsBW = true;
56 fBW.setEmpty();
57 fAA.setEmpty();
reed@google.coma1c6ff42012-05-11 14:36:57 +000058 fIsEmpty = true;
59 fIsRect = false;
reed@google.comd1e3c5f2011-10-10 19:36:25 +000060 return false;
61}
62
reed@google.comba16da92011-10-11 13:15:03 +000063bool SkRasterClip::setRect(const SkIRect& rect) {
reed@google.com045e62d2011-10-24 12:19:46 +000064 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000065
reed@google.comd1e3c5f2011-10-10 19:36:25 +000066 fIsBW = true;
67 fAA.setEmpty();
reed@google.coma1c6ff42012-05-11 14:36:57 +000068 fIsRect = fBW.setRect(rect);
69 fIsEmpty = !fIsRect;
70 return fIsRect;
reed@google.comd1e3c5f2011-10-10 19:36:25 +000071}
72
73bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +000074 AUTO_RASTERCLIP_VALIDATE(*this);
75
reed@google.comd1e3c5f2011-10-10 19:36:25 +000076 if (this->isBW() && !doAA) {
reed@google.coma1c6ff42012-05-11 14:36:57 +000077 (void)fBW.setPath(path, clip);
reed@google.comd1e3c5f2011-10-10 19:36:25 +000078 } else {
reed@google.com897fc412012-02-16 17:11:25 +000079 // TODO: since we are going to over-write fAA completely (aren't we?)
80 // we should just clear our BW data (if any) and set fIsAA=true
reed@google.comd1e3c5f2011-10-10 19:36:25 +000081 if (this->isBW()) {
82 this->convertToAA();
83 }
reed@google.coma1c6ff42012-05-11 14:36:57 +000084 (void)fAA.setPath(path, &clip, doAA);
reed@google.comd1e3c5f2011-10-10 19:36:25 +000085 }
reed@google.coma1c6ff42012-05-11 14:36:57 +000086 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +000087}
88
reedd64c9482014-09-05 17:37:38 -070089bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) {
90 // base is used to limit the size (and therefore memory allocation) of the
91 // region that results from scan converting devPath.
92 SkRegion base;
93
94 if (SkRegion::kIntersect_Op == op) {
95 // since we are intersect, we can do better (tighter) with currRgn's
96 // bounds, than just using the device. However, if currRgn is complex,
97 // our region blitter may hork, so we do that case in two steps.
98 if (this->isRect()) {
99 // FIXME: we should also be able to do this when this->isBW(),
100 // but relaxing the test above triggers GM asserts in
101 // SkRgnBuilder::blitH(). We need to investigate what's going on.
102 return this->setPath(path, this->bwRgn(), doAA);
103 } else {
104 base.setRect(this->getBounds());
105 SkRasterClip clip;
106 clip.setPath(path, base, doAA);
107 return this->op(clip, op);
108 }
109 } else {
110 base.setRect(0, 0, size.width(), size.height());
111
112 if (SkRegion::kReplace_Op == op) {
113 return this->setPath(path, base, doAA);
114 } else {
115 SkRasterClip clip;
116 clip.setPath(path, base, doAA);
117 return this->op(clip, op);
118 }
119 }
120}
121
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000122bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
123 SkRegion tmp;
124 tmp.setRect(clip);
125 return this->setPath(path, tmp, doAA);
126}
127
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000128bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000129 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000130
reed@google.coma1c6ff42012-05-11 14:36:57 +0000131 fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
132 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000133}
134
reed@google.comba16da92011-10-11 13:15:03 +0000135bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000136 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000137
reed@google.comba16da92011-10-11 13:15:03 +0000138 if (fIsBW) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000139 (void)fBW.op(rgn, op);
reed@google.comba16da92011-10-11 13:15:03 +0000140 } else {
141 SkAAClip tmp;
142 tmp.setRegion(rgn);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000143 (void)fAA.op(tmp, op);
reed@google.comba16da92011-10-11 13:15:03 +0000144 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000145 return this->updateCacheAndReturnNonEmpty();
reed@google.comba16da92011-10-11 13:15:03 +0000146}
147
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000148bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000149 AUTO_RASTERCLIP_VALIDATE(*this);
150 clip.validate();
151
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000152 if (this->isBW() && clip.isBW()) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000153 (void)fBW.op(clip.fBW, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000154 } else {
155 SkAAClip tmp;
156 const SkAAClip* other;
157
158 if (this->isBW()) {
159 this->convertToAA();
160 }
161 if (clip.isBW()) {
162 tmp.setRegion(clip.bwRgn());
163 other = &tmp;
164 } else {
165 other = &clip.aaRgn();
166 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000167 (void)fAA.op(*other, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000168 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000169 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000170}
171
reed@google.com18c464b2012-05-11 20:57:25 +0000172/**
173 * Our antialiasing currently has a granularity of 1/4 of a pixel along each
174 * axis. Thus we can treat an axis coordinate as an integer if it differs
175 * from its nearest int by < half of that value (1.8 in this case).
176 */
177static bool nearly_integral(SkScalar x) {
178 static const SkScalar domain = SK_Scalar1 / 4;
179 static const SkScalar halfDomain = domain / 2;
180
181 x += halfDomain;
182 return x - SkScalarFloorToScalar(x) < domain;
reed@google.com00177082011-10-12 14:34:30 +0000183}
184
185bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +0000186 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000187
reed@google.com420f74f2012-05-11 18:46:43 +0000188 if (fIsBW && doAA) {
reed@google.com18c464b2012-05-11 20:57:25 +0000189 // check that the rect really needs aa, or is it close enought to
190 // integer boundaries that we can just treat it as a BW rect?
191 if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
192 nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
reed@google.com00177082011-10-12 14:34:30 +0000193 doAA = false;
194 }
195 }
196
197 if (fIsBW && !doAA) {
198 SkIRect ir;
199 r.round(&ir);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000200 (void)fBW.op(ir, op);
reed@google.com00177082011-10-12 14:34:30 +0000201 } else {
202 if (fIsBW) {
203 this->convertToAA();
204 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000205 (void)fAA.op(r, op, doAA);
reed@google.com00177082011-10-12 14:34:30 +0000206 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000207 return this->updateCacheAndReturnNonEmpty();
reed@google.com00177082011-10-12 14:34:30 +0000208}
209
reed@google.com34f7e472011-10-13 15:11:59 +0000210void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
211 if (NULL == dst) {
212 return;
213 }
214
reed@google.com045e62d2011-10-24 12:19:46 +0000215 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000216
reed@google.com34f7e472011-10-13 15:11:59 +0000217 if (this->isEmpty()) {
218 dst->setEmpty();
219 return;
220 }
221 if (0 == (dx | dy)) {
222 *dst = *this;
223 return;
224 }
225
226 dst->fIsBW = fIsBW;
227 if (fIsBW) {
228 fBW.translate(dx, dy, &dst->fBW);
229 dst->fAA.setEmpty();
230 } else {
231 fAA.translate(dx, dy, &dst->fAA);
232 dst->fBW.setEmpty();
233 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000234 dst->updateCacheAndReturnNonEmpty();
reed@google.com34f7e472011-10-13 15:11:59 +0000235}
236
reed@google.com045e62d2011-10-24 12:19:46 +0000237bool SkRasterClip::quickContains(const SkIRect& ir) const {
238 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
239}
240
reed@google.com34f7e472011-10-13 15:11:59 +0000241///////////////////////////////////////////////////////////////////////////////
242
reed@google.comba16da92011-10-11 13:15:03 +0000243const SkRegion& SkRasterClip::forceGetBW() {
reed@google.com045e62d2011-10-24 12:19:46 +0000244 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000245
reed@google.comba16da92011-10-11 13:15:03 +0000246 if (!fIsBW) {
247 fBW.setRect(fAA.getBounds());
248 }
249 return fBW;
250}
251
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000252void SkRasterClip::convertToAA() {
reed@google.com045e62d2011-10-24 12:19:46 +0000253 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000254
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000255 SkASSERT(fIsBW);
256 fAA.setRegion(fBW);
257 fIsBW = false;
reed202ab2a2014-08-07 11:48:10 -0700258
259 // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
260 // ourselves back to BW.
261 (void)this->updateCacheAndReturnNonEmpty(false);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000262}
263
reed@google.com045e62d2011-10-24 12:19:46 +0000264#ifdef SK_DEBUG
265void SkRasterClip::validate() const {
266 // can't ever assert that fBW is empty, since we may have called forceGetBW
267 if (fIsBW) {
268 SkASSERT(fAA.isEmpty());
269 }
270
271 fBW.validate();
272 fAA.validate();
reed@google.coma1c6ff42012-05-11 14:36:57 +0000273
274 SkASSERT(this->computeIsEmpty() == fIsEmpty);
275 SkASSERT(this->computeIsRect() == fIsRect);
reed@google.com045e62d2011-10-24 12:19:46 +0000276}
277#endif
278
279///////////////////////////////////////////////////////////////////////////////
280
281SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
282 SkDEBUGCODE(fClipRgn = NULL;)
283 SkDEBUGCODE(fBlitter = NULL;)
284}
285
286SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
287 SkBlitter* blitter) {
288 this->init(clip, blitter);
289}
290
291SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
292 SkBlitter* blitter) {
293 SkASSERT(blitter);
294 SkASSERT(aaclip);
295 fBWRgn.setRect(aaclip->getBounds());
296 fAABlitter.init(blitter, aaclip);
297 // now our return values
298 fClipRgn = &fBWRgn;
299 fBlitter = &fAABlitter;
300}
301
302void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
303 SkASSERT(blitter);
304 if (clip.isBW()) {
305 fClipRgn = &clip.bwRgn();
306 fBlitter = blitter;
307 } else {
308 const SkAAClip& aaclip = clip.aaRgn();
309 fBWRgn.setRect(aaclip.getBounds());
310 fAABlitter.init(blitter, &aaclip);
311 // now our return values
312 fClipRgn = &fBWRgn;
313 fBlitter = &fAABlitter;
314 }
315}