blob: f820c5a0a44154389b5cea7521eaef7b1b3aad42 [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"
reedd9544982014-09-09 18:46:22 -07009#include "SkPath.h"
reed@google.comd1e3c5f2011-10-10 19:36:25 +000010
reed@google.com34f7e472011-10-13 15:11:59 +000011SkRasterClip::SkRasterClip(const SkRasterClip& src) {
reed@google.com045e62d2011-10-24 12:19:46 +000012 AUTO_RASTERCLIP_VALIDATE(src);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000013
reedd9544982014-09-09 18:46:22 -070014 fForceConservativeRects = src.fForceConservativeRects;
reed@google.com34f7e472011-10-13 15:11:59 +000015 fIsBW = src.fIsBW;
16 if (fIsBW) {
17 fBW = src.fBW;
18 } else {
19 fAA = src.fAA;
20 }
reed@google.coma1c6ff42012-05-11 14:36:57 +000021
22 fIsEmpty = src.isEmpty();
23 fIsRect = src.isRect();
24 SkDEBUGCODE(this->validate();)
reed@google.com34f7e472011-10-13 15:11:59 +000025}
26
reedd9544982014-09-09 18:46:22 -070027SkRasterClip::SkRasterClip(const SkIRect& bounds, bool forceConservativeRects) : fBW(bounds) {
28 fForceConservativeRects = forceConservativeRects;
reed@google.comba16da92011-10-11 13:15:03 +000029 fIsBW = true;
reed@google.coma1c6ff42012-05-11 14:36:57 +000030 fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
31 fIsRect = !fIsEmpty;
32 SkDEBUGCODE(this->validate();)
reed@google.comba16da92011-10-11 13:15:03 +000033}
34
reedd9544982014-09-09 18:46:22 -070035SkRasterClip::SkRasterClip(bool forceConservativeRects) {
36 fForceConservativeRects = forceConservativeRects;
37 fIsBW = true;
38 fIsEmpty = true;
39 fIsRect = false;
40 SkDEBUGCODE(this->validate();)
41}
42
reed@google.com045e62d2011-10-24 12:19:46 +000043SkRasterClip::~SkRasterClip() {
reed@google.coma1c6ff42012-05-11 14:36:57 +000044 SkDEBUGCODE(this->validate();)
reed@google.comd1e3c5f2011-10-10 19:36:25 +000045}
46
47bool SkRasterClip::isComplex() const {
48 return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
49}
50
51const SkIRect& SkRasterClip::getBounds() const {
52 return fIsBW ? fBW.getBounds() : fAA.getBounds();
53}
54
55bool SkRasterClip::setEmpty() {
reed@google.com045e62d2011-10-24 12:19:46 +000056 AUTO_RASTERCLIP_VALIDATE(*this);
57
reed@google.comd1e3c5f2011-10-10 19:36:25 +000058 fIsBW = true;
59 fBW.setEmpty();
60 fAA.setEmpty();
reed@google.coma1c6ff42012-05-11 14:36:57 +000061 fIsEmpty = true;
62 fIsRect = false;
reed@google.comd1e3c5f2011-10-10 19:36:25 +000063 return false;
64}
65
reed@google.comba16da92011-10-11 13:15:03 +000066bool SkRasterClip::setRect(const SkIRect& rect) {
reed@google.com045e62d2011-10-24 12:19:46 +000067 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000068
reed@google.comd1e3c5f2011-10-10 19:36:25 +000069 fIsBW = true;
70 fAA.setEmpty();
reed@google.coma1c6ff42012-05-11 14:36:57 +000071 fIsRect = fBW.setRect(rect);
72 fIsEmpty = !fIsRect;
73 return fIsRect;
reed@google.comd1e3c5f2011-10-10 19:36:25 +000074}
75
reedd9544982014-09-09 18:46:22 -070076/////////////////////////////////////////////////////////////////////////////////////
77
78bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
79 SkIRect ir;
80 r.roundOut(&ir);
81
82 SkRegion::Op op;
83 if (isInverse) {
84 op = SkRegion::kDifference_Op;
85 } else {
86 op = SkRegion::kIntersect_Op;
87 }
88 fBW.setRect(clipR);
89 fBW.op(ir, op);
90 return this->updateCacheAndReturnNonEmpty();
91}
92
93/////////////////////////////////////////////////////////////////////////////////////
94
95enum MutateResult {
96 kDoNothing_MutateResult,
97 kReplaceClippedAgainstGlobalBounds_MutateResult,
98 kContinue_MutateResult,
99};
100
101static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
102 if (inverseFilled) {
103 switch (*op) {
104 case SkRegion::kIntersect_Op:
105 case SkRegion::kDifference_Op:
106 // These ops can only shrink the current clip. So leaving
107 // the clip unchanged conservatively respects the contract.
108 return kDoNothing_MutateResult;
109 case SkRegion::kUnion_Op:
110 case SkRegion::kReplace_Op:
111 case SkRegion::kReverseDifference_Op:
112 case SkRegion::kXOR_Op: {
113 // These ops can grow the current clip up to the extents of
114 // the input clip, which is inverse filled, so we just set
115 // the current clip to the device bounds.
116 *op = SkRegion::kReplace_Op;
117 return kReplaceClippedAgainstGlobalBounds_MutateResult;
118 }
119 }
120 } else {
121 // Not inverse filled
122 switch (*op) {
123 case SkRegion::kIntersect_Op:
124 case SkRegion::kUnion_Op:
125 case SkRegion::kReplace_Op:
126 return kContinue_MutateResult;
127 case SkRegion::kDifference_Op:
128 // Difference can only shrink the current clip.
129 // Leaving clip unchanged conservatively fullfills the contract.
130 return kDoNothing_MutateResult;
131 case SkRegion::kReverseDifference_Op:
132 // To reverse, we swap in the bounds with a replace op.
133 // As with difference, leave it unchanged.
134 *op = SkRegion::kReplace_Op;
135 return kContinue_MutateResult;
136 case SkRegion::kXOR_Op:
137 // Be conservative, based on (A XOR B) always included in (A union B),
138 // which is always included in (bounds(A) union bounds(B))
139 *op = SkRegion::kUnion_Op;
140 return kContinue_MutateResult;
141 }
142 }
143 SkFAIL("should not get here");
144 return kDoNothing_MutateResult;
145}
146
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000147bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +0000148 AUTO_RASTERCLIP_VALIDATE(*this);
149
reedd9544982014-09-09 18:46:22 -0700150 if (fForceConservativeRects) {
151 return this->setConservativeRect(path.getBounds(), clip.getBounds(), path.isInverseFillType());
152 }
153
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000154 if (this->isBW() && !doAA) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000155 (void)fBW.setPath(path, clip);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000156 } else {
reed@google.com897fc412012-02-16 17:11:25 +0000157 // TODO: since we are going to over-write fAA completely (aren't we?)
158 // we should just clear our BW data (if any) and set fIsAA=true
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000159 if (this->isBW()) {
160 this->convertToAA();
161 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000162 (void)fAA.setPath(path, &clip, doAA);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000163 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000164 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000165}
166
reedd64c9482014-09-05 17:37:38 -0700167bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) {
168 // base is used to limit the size (and therefore memory allocation) of the
169 // region that results from scan converting devPath.
170 SkRegion base;
reedd9544982014-09-09 18:46:22 -0700171
172 if (fForceConservativeRects) {
173 SkIRect ir;
174 switch (mutate_conservative_op(&op, path.isInverseFillType())) {
175 case kDoNothing_MutateResult:
176 return !this->isEmpty();
177 case kReplaceClippedAgainstGlobalBounds_MutateResult:
178 ir = SkIRect::MakeSize(size);
179 break;
180 case kContinue_MutateResult:
181 path.getBounds().roundOut(&ir);
182 break;
183 }
184 return this->op(ir, op);
185 }
186
reedd64c9482014-09-05 17:37:38 -0700187 if (SkRegion::kIntersect_Op == op) {
188 // since we are intersect, we can do better (tighter) with currRgn's
189 // bounds, than just using the device. However, if currRgn is complex,
190 // our region blitter may hork, so we do that case in two steps.
191 if (this->isRect()) {
192 // FIXME: we should also be able to do this when this->isBW(),
193 // but relaxing the test above triggers GM asserts in
194 // SkRgnBuilder::blitH(). We need to investigate what's going on.
195 return this->setPath(path, this->bwRgn(), doAA);
196 } else {
197 base.setRect(this->getBounds());
reedd9544982014-09-09 18:46:22 -0700198 SkRasterClip clip(fForceConservativeRects);
reedd64c9482014-09-05 17:37:38 -0700199 clip.setPath(path, base, doAA);
200 return this->op(clip, op);
201 }
202 } else {
203 base.setRect(0, 0, size.width(), size.height());
204
205 if (SkRegion::kReplace_Op == op) {
206 return this->setPath(path, base, doAA);
207 } else {
reedd9544982014-09-09 18:46:22 -0700208 SkRasterClip clip(fForceConservativeRects);
reedd64c9482014-09-05 17:37:38 -0700209 clip.setPath(path, base, doAA);
210 return this->op(clip, op);
211 }
212 }
213}
214
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000215bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
216 SkRegion tmp;
217 tmp.setRect(clip);
218 return this->setPath(path, tmp, doAA);
219}
220
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000221bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000222 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000223
reed@google.coma1c6ff42012-05-11 14:36:57 +0000224 fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
225 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000226}
227
reed@google.comba16da92011-10-11 13:15:03 +0000228bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000229 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000230
reed@google.comba16da92011-10-11 13:15:03 +0000231 if (fIsBW) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000232 (void)fBW.op(rgn, op);
reed@google.comba16da92011-10-11 13:15:03 +0000233 } else {
234 SkAAClip tmp;
235 tmp.setRegion(rgn);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000236 (void)fAA.op(tmp, op);
reed@google.comba16da92011-10-11 13:15:03 +0000237 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000238 return this->updateCacheAndReturnNonEmpty();
reed@google.comba16da92011-10-11 13:15:03 +0000239}
240
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000241bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000242 AUTO_RASTERCLIP_VALIDATE(*this);
243 clip.validate();
244
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000245 if (this->isBW() && clip.isBW()) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000246 (void)fBW.op(clip.fBW, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000247 } else {
248 SkAAClip tmp;
249 const SkAAClip* other;
250
251 if (this->isBW()) {
252 this->convertToAA();
253 }
254 if (clip.isBW()) {
255 tmp.setRegion(clip.bwRgn());
256 other = &tmp;
257 } else {
258 other = &clip.aaRgn();
259 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000260 (void)fAA.op(*other, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000261 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000262 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000263}
264
reed@google.com18c464b2012-05-11 20:57:25 +0000265/**
266 * Our antialiasing currently has a granularity of 1/4 of a pixel along each
267 * axis. Thus we can treat an axis coordinate as an integer if it differs
268 * from its nearest int by < half of that value (1.8 in this case).
269 */
270static bool nearly_integral(SkScalar x) {
271 static const SkScalar domain = SK_Scalar1 / 4;
272 static const SkScalar halfDomain = domain / 2;
273
274 x += halfDomain;
275 return x - SkScalarFloorToScalar(x) < domain;
reed@google.com00177082011-10-12 14:34:30 +0000276}
277
reedd9544982014-09-09 18:46:22 -0700278bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +0000279 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000280
reedd9544982014-09-09 18:46:22 -0700281 if (fForceConservativeRects) {
282 SkIRect ir;
283 switch (mutate_conservative_op(&op, false)) {
284 case kDoNothing_MutateResult:
285 return !this->isEmpty();
286 case kReplaceClippedAgainstGlobalBounds_MutateResult:
287 ir = SkIRect::MakeSize(size);
288 break;
289 case kContinue_MutateResult:
290 r.roundOut(&ir);
291 break;
292 }
293 return this->op(ir, op);
294 }
295
reed@google.com420f74f2012-05-11 18:46:43 +0000296 if (fIsBW && doAA) {
reed@google.com18c464b2012-05-11 20:57:25 +0000297 // check that the rect really needs aa, or is it close enought to
298 // integer boundaries that we can just treat it as a BW rect?
299 if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
300 nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
reed@google.com00177082011-10-12 14:34:30 +0000301 doAA = false;
302 }
303 }
304
305 if (fIsBW && !doAA) {
306 SkIRect ir;
307 r.round(&ir);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000308 (void)fBW.op(ir, op);
reed@google.com00177082011-10-12 14:34:30 +0000309 } else {
310 if (fIsBW) {
311 this->convertToAA();
312 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000313 (void)fAA.op(r, op, doAA);
reed@google.com00177082011-10-12 14:34:30 +0000314 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000315 return this->updateCacheAndReturnNonEmpty();
reed@google.com00177082011-10-12 14:34:30 +0000316}
317
reed@google.com34f7e472011-10-13 15:11:59 +0000318void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
319 if (NULL == dst) {
320 return;
321 }
322
reed@google.com045e62d2011-10-24 12:19:46 +0000323 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000324
reed@google.com34f7e472011-10-13 15:11:59 +0000325 if (this->isEmpty()) {
326 dst->setEmpty();
327 return;
328 }
329 if (0 == (dx | dy)) {
330 *dst = *this;
331 return;
332 }
333
334 dst->fIsBW = fIsBW;
335 if (fIsBW) {
336 fBW.translate(dx, dy, &dst->fBW);
337 dst->fAA.setEmpty();
338 } else {
339 fAA.translate(dx, dy, &dst->fAA);
340 dst->fBW.setEmpty();
341 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000342 dst->updateCacheAndReturnNonEmpty();
reed@google.com34f7e472011-10-13 15:11:59 +0000343}
344
reed@google.com045e62d2011-10-24 12:19:46 +0000345bool SkRasterClip::quickContains(const SkIRect& ir) const {
346 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
347}
348
reed@google.com34f7e472011-10-13 15:11:59 +0000349///////////////////////////////////////////////////////////////////////////////
350
reed@google.comba16da92011-10-11 13:15:03 +0000351const SkRegion& SkRasterClip::forceGetBW() {
reed@google.com045e62d2011-10-24 12:19:46 +0000352 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000353
reed@google.comba16da92011-10-11 13:15:03 +0000354 if (!fIsBW) {
355 fBW.setRect(fAA.getBounds());
356 }
357 return fBW;
358}
359
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000360void SkRasterClip::convertToAA() {
reed@google.com045e62d2011-10-24 12:19:46 +0000361 AUTO_RASTERCLIP_VALIDATE(*this);
reedd9544982014-09-09 18:46:22 -0700362
363 SkASSERT(!fForceConservativeRects);
364
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000365 SkASSERT(fIsBW);
366 fAA.setRegion(fBW);
367 fIsBW = false;
reed202ab2a2014-08-07 11:48:10 -0700368
369 // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
370 // ourselves back to BW.
371 (void)this->updateCacheAndReturnNonEmpty(false);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000372}
373
reed@google.com045e62d2011-10-24 12:19:46 +0000374#ifdef SK_DEBUG
375void SkRasterClip::validate() const {
376 // can't ever assert that fBW is empty, since we may have called forceGetBW
377 if (fIsBW) {
378 SkASSERT(fAA.isEmpty());
379 }
380
381 fBW.validate();
382 fAA.validate();
reed@google.coma1c6ff42012-05-11 14:36:57 +0000383
384 SkASSERT(this->computeIsEmpty() == fIsEmpty);
385 SkASSERT(this->computeIsRect() == fIsRect);
reed@google.com045e62d2011-10-24 12:19:46 +0000386}
387#endif
388
389///////////////////////////////////////////////////////////////////////////////
390
391SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
392 SkDEBUGCODE(fClipRgn = NULL;)
393 SkDEBUGCODE(fBlitter = NULL;)
394}
395
396SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
397 SkBlitter* blitter) {
398 this->init(clip, blitter);
399}
400
401SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
402 SkBlitter* blitter) {
403 SkASSERT(blitter);
404 SkASSERT(aaclip);
405 fBWRgn.setRect(aaclip->getBounds());
406 fAABlitter.init(blitter, aaclip);
407 // now our return values
408 fClipRgn = &fBWRgn;
409 fBlitter = &fAABlitter;
410}
411
412void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
413 SkASSERT(blitter);
414 if (clip.isBW()) {
415 fClipRgn = &clip.bwRgn();
416 fBlitter = blitter;
417 } else {
418 const SkAAClip& aaclip = clip.aaRgn();
419 fBWRgn.setRect(aaclip.getBounds());
420 fAABlitter.init(blitter, &aaclip);
421 // now our return values
422 fClipRgn = &fBWRgn;
423 fBlitter = &fAABlitter;
424 }
425}