blob: 89467677f3ec95904ff4ae540a82b031f2310bae [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) {
reedd9544982014-09-09 18:46:22 -070079 SkRegion::Op op;
80 if (isInverse) {
81 op = SkRegion::kDifference_Op;
82 } else {
83 op = SkRegion::kIntersect_Op;
84 }
85 fBW.setRect(clipR);
reedb07a94f2014-11-19 05:03:18 -080086 fBW.op(r.roundOut(), op);
reedd9544982014-09-09 18:46:22 -070087 return this->updateCacheAndReturnNonEmpty();
88}
89
90/////////////////////////////////////////////////////////////////////////////////////
91
92enum MutateResult {
93 kDoNothing_MutateResult,
94 kReplaceClippedAgainstGlobalBounds_MutateResult,
95 kContinue_MutateResult,
96};
97
98static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
99 if (inverseFilled) {
100 switch (*op) {
101 case SkRegion::kIntersect_Op:
102 case SkRegion::kDifference_Op:
103 // These ops can only shrink the current clip. So leaving
104 // the clip unchanged conservatively respects the contract.
105 return kDoNothing_MutateResult;
106 case SkRegion::kUnion_Op:
107 case SkRegion::kReplace_Op:
108 case SkRegion::kReverseDifference_Op:
109 case SkRegion::kXOR_Op: {
110 // These ops can grow the current clip up to the extents of
111 // the input clip, which is inverse filled, so we just set
112 // the current clip to the device bounds.
113 *op = SkRegion::kReplace_Op;
114 return kReplaceClippedAgainstGlobalBounds_MutateResult;
115 }
116 }
117 } else {
118 // Not inverse filled
119 switch (*op) {
120 case SkRegion::kIntersect_Op:
121 case SkRegion::kUnion_Op:
122 case SkRegion::kReplace_Op:
123 return kContinue_MutateResult;
124 case SkRegion::kDifference_Op:
125 // Difference can only shrink the current clip.
126 // Leaving clip unchanged conservatively fullfills the contract.
127 return kDoNothing_MutateResult;
128 case SkRegion::kReverseDifference_Op:
129 // To reverse, we swap in the bounds with a replace op.
130 // As with difference, leave it unchanged.
131 *op = SkRegion::kReplace_Op;
132 return kContinue_MutateResult;
133 case SkRegion::kXOR_Op:
134 // Be conservative, based on (A XOR B) always included in (A union B),
135 // which is always included in (bounds(A) union bounds(B))
136 *op = SkRegion::kUnion_Op;
137 return kContinue_MutateResult;
138 }
139 }
140 SkFAIL("should not get here");
141 return kDoNothing_MutateResult;
142}
143
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000144bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +0000145 AUTO_RASTERCLIP_VALIDATE(*this);
146
reedd9544982014-09-09 18:46:22 -0700147 if (fForceConservativeRects) {
148 return this->setConservativeRect(path.getBounds(), clip.getBounds(), path.isInverseFillType());
149 }
150
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000151 if (this->isBW() && !doAA) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000152 (void)fBW.setPath(path, clip);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000153 } else {
reed@google.com897fc412012-02-16 17:11:25 +0000154 // TODO: since we are going to over-write fAA completely (aren't we?)
155 // we should just clear our BW data (if any) and set fIsAA=true
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000156 if (this->isBW()) {
157 this->convertToAA();
158 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000159 (void)fAA.setPath(path, &clip, doAA);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000160 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000161 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000162}
163
senorblancoafc7cce2016-02-02 18:44:15 -0800164bool SkRasterClip::op(const SkRRect& rrect, const SkIRect& bounds, SkRegion::Op op, bool doAA) {
robertphillips125f19a2015-11-23 09:00:05 -0800165 if (fForceConservativeRects) {
senorblancoafc7cce2016-02-02 18:44:15 -0800166 return this->op(rrect.getBounds(), bounds, op, doAA);
robertphillips125f19a2015-11-23 09:00:05 -0800167 }
168
169 SkPath path;
170 path.addRRect(rrect);
171
senorblancoafc7cce2016-02-02 18:44:15 -0800172 return this->op(path, bounds, op, doAA);
robertphillips125f19a2015-11-23 09:00:05 -0800173}
174
senorblancoafc7cce2016-02-02 18:44:15 -0800175bool SkRasterClip::op(const SkPath& path, const SkIRect& bounds, SkRegion::Op op, bool doAA) {
robertphillips125f19a2015-11-23 09:00:05 -0800176 AUTO_RASTERCLIP_VALIDATE(*this);
reedd9544982014-09-09 18:46:22 -0700177
178 if (fForceConservativeRects) {
179 SkIRect ir;
180 switch (mutate_conservative_op(&op, path.isInverseFillType())) {
181 case kDoNothing_MutateResult:
182 return !this->isEmpty();
183 case kReplaceClippedAgainstGlobalBounds_MutateResult:
senorblancoafc7cce2016-02-02 18:44:15 -0800184 ir = bounds;
reedd9544982014-09-09 18:46:22 -0700185 break;
186 case kContinue_MutateResult:
reedb07a94f2014-11-19 05:03:18 -0800187 ir = path.getBounds().roundOut();
reedd9544982014-09-09 18:46:22 -0700188 break;
189 }
190 return this->op(ir, op);
191 }
192
robertphillips125f19a2015-11-23 09:00:05 -0800193 // base is used to limit the size (and therefore memory allocation) of the
194 // region that results from scan converting devPath.
195 SkRegion base;
196
reedd64c9482014-09-05 17:37:38 -0700197 if (SkRegion::kIntersect_Op == op) {
198 // since we are intersect, we can do better (tighter) with currRgn's
199 // bounds, than just using the device. However, if currRgn is complex,
200 // our region blitter may hork, so we do that case in two steps.
201 if (this->isRect()) {
202 // FIXME: we should also be able to do this when this->isBW(),
203 // but relaxing the test above triggers GM asserts in
204 // SkRgnBuilder::blitH(). We need to investigate what's going on.
205 return this->setPath(path, this->bwRgn(), doAA);
206 } else {
207 base.setRect(this->getBounds());
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 } else {
senorblancoafc7cce2016-02-02 18:44:15 -0800213 base.setRect(bounds);
halcanary9d524f22016-03-29 09:03:52 -0700214
reedd64c9482014-09-05 17:37:38 -0700215 if (SkRegion::kReplace_Op == op) {
216 return this->setPath(path, base, doAA);
217 } else {
reedd9544982014-09-09 18:46:22 -0700218 SkRasterClip clip(fForceConservativeRects);
reedd64c9482014-09-05 17:37:38 -0700219 clip.setPath(path, base, doAA);
220 return this->op(clip, op);
221 }
222 }
223}
224
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000225bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
226 SkRegion tmp;
227 tmp.setRect(clip);
228 return this->setPath(path, tmp, doAA);
229}
230
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000231bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000232 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000233
reed@google.coma1c6ff42012-05-11 14:36:57 +0000234 fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
235 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000236}
237
reed@google.comba16da92011-10-11 13:15:03 +0000238bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000239 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000240
reed@google.comba16da92011-10-11 13:15:03 +0000241 if (fIsBW) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000242 (void)fBW.op(rgn, op);
reed@google.comba16da92011-10-11 13:15:03 +0000243 } else {
244 SkAAClip tmp;
245 tmp.setRegion(rgn);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000246 (void)fAA.op(tmp, op);
reed@google.comba16da92011-10-11 13:15:03 +0000247 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000248 return this->updateCacheAndReturnNonEmpty();
reed@google.comba16da92011-10-11 13:15:03 +0000249}
250
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000251bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
reed@google.com045e62d2011-10-24 12:19:46 +0000252 AUTO_RASTERCLIP_VALIDATE(*this);
253 clip.validate();
254
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000255 if (this->isBW() && clip.isBW()) {
reed@google.coma1c6ff42012-05-11 14:36:57 +0000256 (void)fBW.op(clip.fBW, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000257 } else {
258 SkAAClip tmp;
259 const SkAAClip* other;
260
261 if (this->isBW()) {
262 this->convertToAA();
263 }
264 if (clip.isBW()) {
265 tmp.setRegion(clip.bwRgn());
266 other = &tmp;
267 } else {
268 other = &clip.aaRgn();
269 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000270 (void)fAA.op(*other, op);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000271 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000272 return this->updateCacheAndReturnNonEmpty();
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000273}
274
reed@google.com18c464b2012-05-11 20:57:25 +0000275/**
276 * Our antialiasing currently has a granularity of 1/4 of a pixel along each
277 * axis. Thus we can treat an axis coordinate as an integer if it differs
278 * from its nearest int by < half of that value (1.8 in this case).
279 */
280static bool nearly_integral(SkScalar x) {
281 static const SkScalar domain = SK_Scalar1 / 4;
282 static const SkScalar halfDomain = domain / 2;
283
284 x += halfDomain;
285 return x - SkScalarFloorToScalar(x) < domain;
reed@google.com00177082011-10-12 14:34:30 +0000286}
287
senorblancoafc7cce2016-02-02 18:44:15 -0800288bool SkRasterClip::op(const SkRect& r, const SkIRect& bounds, SkRegion::Op op, bool doAA) {
reed@google.com045e62d2011-10-24 12:19:46 +0000289 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000290
reedd9544982014-09-09 18:46:22 -0700291 if (fForceConservativeRects) {
292 SkIRect ir;
293 switch (mutate_conservative_op(&op, false)) {
294 case kDoNothing_MutateResult:
295 return !this->isEmpty();
296 case kReplaceClippedAgainstGlobalBounds_MutateResult:
senorblancoafc7cce2016-02-02 18:44:15 -0800297 ir = bounds;
reedd9544982014-09-09 18:46:22 -0700298 break;
299 case kContinue_MutateResult:
reedb07a94f2014-11-19 05:03:18 -0800300 ir = r.roundOut();
reedd9544982014-09-09 18:46:22 -0700301 break;
302 }
303 return this->op(ir, op);
304 }
halcanary9d524f22016-03-29 09:03:52 -0700305
reed@google.com420f74f2012-05-11 18:46:43 +0000306 if (fIsBW && doAA) {
reed@google.com18c464b2012-05-11 20:57:25 +0000307 // check that the rect really needs aa, or is it close enought to
308 // integer boundaries that we can just treat it as a BW rect?
309 if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
310 nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
reed@google.com00177082011-10-12 14:34:30 +0000311 doAA = false;
312 }
313 }
314
315 if (fIsBW && !doAA) {
316 SkIRect ir;
317 r.round(&ir);
reed@google.coma1c6ff42012-05-11 14:36:57 +0000318 (void)fBW.op(ir, op);
reed@google.com00177082011-10-12 14:34:30 +0000319 } else {
320 if (fIsBW) {
321 this->convertToAA();
322 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000323 (void)fAA.op(r, op, doAA);
reed@google.com00177082011-10-12 14:34:30 +0000324 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000325 return this->updateCacheAndReturnNonEmpty();
reed@google.com00177082011-10-12 14:34:30 +0000326}
327
reed@google.com34f7e472011-10-13 15:11:59 +0000328void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700329 if (nullptr == dst) {
reed@google.com34f7e472011-10-13 15:11:59 +0000330 return;
331 }
332
reed@google.com045e62d2011-10-24 12:19:46 +0000333 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000334
reed@google.com34f7e472011-10-13 15:11:59 +0000335 if (this->isEmpty()) {
336 dst->setEmpty();
337 return;
338 }
339 if (0 == (dx | dy)) {
340 *dst = *this;
341 return;
342 }
343
344 dst->fIsBW = fIsBW;
345 if (fIsBW) {
346 fBW.translate(dx, dy, &dst->fBW);
347 dst->fAA.setEmpty();
348 } else {
349 fAA.translate(dx, dy, &dst->fAA);
350 dst->fBW.setEmpty();
351 }
reed@google.coma1c6ff42012-05-11 14:36:57 +0000352 dst->updateCacheAndReturnNonEmpty();
reed@google.com34f7e472011-10-13 15:11:59 +0000353}
354
reed@google.com045e62d2011-10-24 12:19:46 +0000355bool SkRasterClip::quickContains(const SkIRect& ir) const {
356 return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
357}
358
reed@google.com34f7e472011-10-13 15:11:59 +0000359///////////////////////////////////////////////////////////////////////////////
360
reed@google.comba16da92011-10-11 13:15:03 +0000361const SkRegion& SkRasterClip::forceGetBW() {
reed@google.com045e62d2011-10-24 12:19:46 +0000362 AUTO_RASTERCLIP_VALIDATE(*this);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000363
reed@google.comba16da92011-10-11 13:15:03 +0000364 if (!fIsBW) {
365 fBW.setRect(fAA.getBounds());
366 }
367 return fBW;
368}
369
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000370void SkRasterClip::convertToAA() {
reed@google.com045e62d2011-10-24 12:19:46 +0000371 AUTO_RASTERCLIP_VALIDATE(*this);
halcanary9d524f22016-03-29 09:03:52 -0700372
reedd9544982014-09-09 18:46:22 -0700373 SkASSERT(!fForceConservativeRects);
halcanary9d524f22016-03-29 09:03:52 -0700374
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000375 SkASSERT(fIsBW);
376 fAA.setRegion(fBW);
377 fIsBW = false;
halcanary9d524f22016-03-29 09:03:52 -0700378
reed202ab2a2014-08-07 11:48:10 -0700379 // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
380 // ourselves back to BW.
381 (void)this->updateCacheAndReturnNonEmpty(false);
reed@google.comd1e3c5f2011-10-10 19:36:25 +0000382}
383
reed@google.com045e62d2011-10-24 12:19:46 +0000384#ifdef SK_DEBUG
385void SkRasterClip::validate() const {
386 // can't ever assert that fBW is empty, since we may have called forceGetBW
387 if (fIsBW) {
388 SkASSERT(fAA.isEmpty());
389 }
390
391 fBW.validate();
392 fAA.validate();
reed@google.coma1c6ff42012-05-11 14:36:57 +0000393
394 SkASSERT(this->computeIsEmpty() == fIsEmpty);
395 SkASSERT(this->computeIsRect() == fIsRect);
reed@google.com045e62d2011-10-24 12:19:46 +0000396}
397#endif
398
399///////////////////////////////////////////////////////////////////////////////
400
401SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
halcanary96fcdcc2015-08-27 07:41:13 -0700402 SkDEBUGCODE(fClipRgn = nullptr;)
403 SkDEBUGCODE(fBlitter = nullptr;)
reed@google.com045e62d2011-10-24 12:19:46 +0000404}
405
406SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
407 SkBlitter* blitter) {
408 this->init(clip, blitter);
409}
410
411SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
412 SkBlitter* blitter) {
413 SkASSERT(blitter);
414 SkASSERT(aaclip);
415 fBWRgn.setRect(aaclip->getBounds());
416 fAABlitter.init(blitter, aaclip);
417 // now our return values
418 fClipRgn = &fBWRgn;
419 fBlitter = &fAABlitter;
420}
421
422void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
423 SkASSERT(blitter);
424 if (clip.isBW()) {
425 fClipRgn = &clip.bwRgn();
426 fBlitter = blitter;
427 } else {
428 const SkAAClip& aaclip = clip.aaRgn();
429 fBWRgn.setRect(aaclip.getBounds());
430 fAABlitter.init(blitter, &aaclip);
431 // now our return values
432 fClipRgn = &fBWRgn;
433 fBlitter = &fAABlitter;
434 }
435}