Line data Source code
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 "SkRasterClip.h"
9 : #include "SkPath.h"
10 :
11 : enum MutateResult {
12 : kDoNothing_MutateResult,
13 : kReplaceClippedAgainstGlobalBounds_MutateResult,
14 : kContinue_MutateResult,
15 : };
16 :
17 571 : static MutateResult mutate_conservative_op(SkRegion::Op* op, bool inverseFilled) {
18 571 : if (inverseFilled) {
19 0 : switch (*op) {
20 : case SkRegion::kIntersect_Op:
21 : case SkRegion::kDifference_Op:
22 : // These ops can only shrink the current clip. So leaving
23 : // the clip unchanged conservatively respects the contract.
24 0 : return kDoNothing_MutateResult;
25 : case SkRegion::kUnion_Op:
26 : case SkRegion::kReplace_Op:
27 : case SkRegion::kReverseDifference_Op:
28 : case SkRegion::kXOR_Op: {
29 : // These ops can grow the current clip up to the extents of
30 : // the input clip, which is inverse filled, so we just set
31 : // the current clip to the device bounds.
32 0 : *op = SkRegion::kReplace_Op;
33 0 : return kReplaceClippedAgainstGlobalBounds_MutateResult;
34 : }
35 : }
36 : } else {
37 : // Not inverse filled
38 571 : switch (*op) {
39 : case SkRegion::kIntersect_Op:
40 : case SkRegion::kUnion_Op:
41 : case SkRegion::kReplace_Op:
42 571 : return kContinue_MutateResult;
43 : case SkRegion::kDifference_Op:
44 : // Difference can only shrink the current clip.
45 : // Leaving clip unchanged conservatively fullfills the contract.
46 0 : return kDoNothing_MutateResult;
47 : case SkRegion::kReverseDifference_Op:
48 : // To reverse, we swap in the bounds with a replace op.
49 : // As with difference, leave it unchanged.
50 0 : *op = SkRegion::kReplace_Op;
51 0 : return kContinue_MutateResult;
52 : case SkRegion::kXOR_Op:
53 : // Be conservative, based on (A XOR B) always included in (A union B),
54 : // which is always included in (bounds(A) union bounds(B))
55 0 : *op = SkRegion::kUnion_Op;
56 0 : return kContinue_MutateResult;
57 : }
58 : }
59 0 : SkFAIL("should not get here");
60 0 : return kDoNothing_MutateResult;
61 : }
62 :
63 532 : void SkConservativeClip::op(const SkRect& localRect, const SkMatrix& ctm, const SkIRect& devBounds,
64 : SkRegion::Op op, bool doAA) {
65 : SkIRect ir;
66 532 : switch (mutate_conservative_op(&op, false)) {
67 : case kDoNothing_MutateResult:
68 0 : return;
69 : case kReplaceClippedAgainstGlobalBounds_MutateResult:
70 0 : ir = devBounds;
71 0 : break;
72 : case kContinue_MutateResult: {
73 : SkRect devRect;
74 532 : ctm.mapRect(&devRect, localRect);
75 532 : ir = doAA ? devRect.roundOut() : devRect.round();
76 532 : } break;
77 : }
78 532 : this->op(ir, op);
79 : }
80 :
81 0 : void SkConservativeClip::op(const SkRRect& rrect, const SkMatrix& ctm, const SkIRect& devBounds,
82 : SkRegion::Op op, bool doAA) {
83 0 : this->op(rrect.getBounds(), ctm, devBounds, op, doAA);
84 0 : }
85 :
86 39 : void SkConservativeClip::op(const SkPath& path, const SkMatrix& ctm, const SkIRect& devBounds,
87 : SkRegion::Op op, bool doAA) {
88 : SkIRect ir;
89 39 : switch (mutate_conservative_op(&op, path.isInverseFillType())) {
90 : case kDoNothing_MutateResult:
91 0 : return;
92 : case kReplaceClippedAgainstGlobalBounds_MutateResult:
93 0 : ir = devBounds;
94 0 : break;
95 : case kContinue_MutateResult: {
96 39 : SkRect bounds = path.getBounds();
97 39 : ctm.mapRect(&bounds);
98 39 : ir = bounds.roundOut();
99 39 : break;
100 : }
101 : }
102 39 : return this->op(ir, op);
103 : }
104 :
105 24 : void SkConservativeClip::op(const SkRegion& rgn, SkRegion::Op op) {
106 24 : this->op(rgn.getBounds(), op);
107 24 : }
108 :
109 595 : void SkConservativeClip::op(const SkIRect& devRect, SkRegion::Op op) {
110 595 : if (SkRegion::kIntersect_Op == op) {
111 595 : if (!fBounds.intersect(devRect)) {
112 15 : fBounds.setEmpty();
113 : }
114 595 : return;
115 : }
116 :
117 : // This may still create a complex region (which we would then take the bounds
118 : // Perhaps we should inline the op-logic directly to never create the rgn...
119 0 : SkRegion result;
120 0 : result.op(SkRegion(fBounds), SkRegion(devRect), op);
121 0 : fBounds = result.getBounds();
122 0 : this->applyClipRestriction(op, &fBounds);
123 : }
124 :
125 : ///////////////////////////////////////////////////////////////////////////////////////////////////
126 :
127 595 : SkRasterClip::SkRasterClip(const SkRasterClip& src) {
128 1190 : AUTO_RASTERCLIP_VALIDATE(src);
129 :
130 595 : fIsBW = src.fIsBW;
131 595 : if (fIsBW) {
132 473 : fBW = src.fBW;
133 : } else {
134 122 : fAA = src.fAA;
135 : }
136 :
137 595 : fIsEmpty = src.isEmpty();
138 595 : fIsRect = src.isRect();
139 595 : fClipRestrictionRect = src.fClipRestrictionRect;
140 595 : SkDEBUGCODE(this->validate();)
141 595 : }
142 :
143 0 : SkRasterClip::SkRasterClip(const SkRegion& rgn) : fBW(rgn) {
144 0 : fIsBW = true;
145 0 : fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
146 0 : fIsRect = !fIsEmpty;
147 0 : SkDEBUGCODE(this->validate();)
148 0 : }
149 :
150 0 : SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
151 0 : fIsBW = true;
152 0 : fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
153 0 : fIsRect = !fIsEmpty;
154 0 : SkDEBUGCODE(this->validate();)
155 0 : }
156 :
157 396 : SkRasterClip::SkRasterClip() {
158 396 : fIsBW = true;
159 396 : fIsEmpty = true;
160 396 : fIsRect = false;
161 396 : SkDEBUGCODE(this->validate();)
162 396 : }
163 :
164 1878 : SkRasterClip::~SkRasterClip() {
165 939 : SkDEBUGCODE(this->validate();)
166 939 : }
167 :
168 0 : bool SkRasterClip::operator==(const SkRasterClip& other) const {
169 0 : if (fIsBW != other.fIsBW) {
170 0 : return false;
171 : }
172 0 : bool isEqual = fIsBW ? fBW == other.fBW : fAA == other.fAA;
173 : #ifdef SK_DEBUG
174 0 : if (isEqual) {
175 0 : SkASSERT(fIsEmpty == other.fIsEmpty);
176 0 : SkASSERT(fIsRect == other.fIsRect);
177 : }
178 : #endif
179 0 : return isEqual;
180 : }
181 :
182 0 : bool SkRasterClip::isComplex() const {
183 0 : return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
184 : }
185 :
186 1981 : const SkIRect& SkRasterClip::getBounds() const {
187 1981 : return fIsBW ? fBW.getBounds() : fAA.getBounds();
188 : }
189 :
190 0 : bool SkRasterClip::setEmpty() {
191 0 : AUTO_RASTERCLIP_VALIDATE(*this);
192 :
193 0 : fIsBW = true;
194 0 : fBW.setEmpty();
195 0 : fAA.setEmpty();
196 0 : fIsEmpty = true;
197 0 : fIsRect = false;
198 0 : return false;
199 : }
200 :
201 88 : bool SkRasterClip::setRect(const SkIRect& rect) {
202 176 : AUTO_RASTERCLIP_VALIDATE(*this);
203 :
204 88 : fIsBW = true;
205 88 : fAA.setEmpty();
206 88 : fIsRect = fBW.setRect(rect);
207 88 : fIsEmpty = !fIsRect;
208 176 : return fIsRect;
209 : }
210 :
211 : /////////////////////////////////////////////////////////////////////////////////////
212 :
213 0 : bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
214 : SkRegion::Op op;
215 0 : if (isInverse) {
216 0 : op = SkRegion::kDifference_Op;
217 : } else {
218 0 : op = SkRegion::kIntersect_Op;
219 : }
220 0 : fBW.setRect(clipR);
221 0 : fBW.op(r.roundOut(), op);
222 0 : return this->updateCacheAndReturnNonEmpty();
223 : }
224 :
225 : /////////////////////////////////////////////////////////////////////////////////////
226 :
227 39 : bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
228 78 : AUTO_RASTERCLIP_VALIDATE(*this);
229 :
230 39 : if (this->isBW() && !doAA) {
231 0 : (void)fBW.setPath(path, clip);
232 : } else {
233 : // TODO: since we are going to over-write fAA completely (aren't we?)
234 : // we should just clear our BW data (if any) and set fIsAA=true
235 39 : if (this->isBW()) {
236 39 : this->convertToAA();
237 : }
238 39 : (void)fAA.setPath(path, &clip, doAA);
239 : }
240 78 : return this->updateCacheAndReturnNonEmpty();
241 : }
242 :
243 0 : bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
244 : SkRegion::Op op, bool doAA) {
245 0 : SkIRect bounds(devBounds);
246 0 : this->applyClipRestriction(op, &bounds);
247 :
248 0 : SkPath path;
249 0 : path.addRRect(rrect);
250 :
251 0 : return this->op(path, matrix, bounds, op, doAA);
252 : }
253 :
254 39 : bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
255 : SkRegion::Op op, bool doAA) {
256 78 : AUTO_RASTERCLIP_VALIDATE(*this);
257 39 : SkIRect bounds(devBounds);
258 39 : this->applyClipRestriction(op, &bounds);
259 :
260 : // base is used to limit the size (and therefore memory allocation) of the
261 : // region that results from scan converting devPath.
262 78 : SkRegion base;
263 :
264 78 : SkPath devPath;
265 39 : if (matrix.isIdentity()) {
266 25 : devPath = path;
267 : } else {
268 14 : path.transform(matrix, &devPath);
269 14 : devPath.setIsVolatile(true);
270 : }
271 39 : if (SkRegion::kIntersect_Op == op) {
272 : // since we are intersect, we can do better (tighter) with currRgn's
273 : // bounds, than just using the device. However, if currRgn is complex,
274 : // our region blitter may hork, so we do that case in two steps.
275 39 : if (this->isRect()) {
276 : // FIXME: we should also be able to do this when this->isBW(),
277 : // but relaxing the test above triggers GM asserts in
278 : // SkRgnBuilder::blitH(). We need to investigate what's going on.
279 29 : return this->setPath(devPath, this->bwRgn(), doAA);
280 : } else {
281 10 : base.setRect(this->getBounds());
282 20 : SkRasterClip clip;
283 10 : clip.setPath(devPath, base, doAA);
284 10 : return this->op(clip, op);
285 : }
286 : } else {
287 0 : base.setRect(bounds);
288 :
289 0 : if (SkRegion::kReplace_Op == op) {
290 0 : return this->setPath(devPath, base, doAA);
291 : } else {
292 0 : SkRasterClip clip;
293 0 : clip.setPath(devPath, base, doAA);
294 0 : return this->op(clip, op);
295 : }
296 : }
297 : }
298 :
299 0 : bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
300 0 : SkRegion tmp;
301 0 : tmp.setRect(clip);
302 0 : return this->setPath(path, tmp, doAA);
303 : }
304 :
305 0 : bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
306 0 : AUTO_RASTERCLIP_VALIDATE(*this);
307 :
308 0 : fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
309 0 : return this->updateCacheAndReturnNonEmpty();
310 : }
311 :
312 24 : bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
313 48 : AUTO_RASTERCLIP_VALIDATE(*this);
314 :
315 24 : if (fIsBW) {
316 24 : (void)fBW.op(rgn, op);
317 : } else {
318 0 : SkAAClip tmp;
319 0 : tmp.setRegion(rgn);
320 0 : (void)fAA.op(tmp, op);
321 : }
322 48 : return this->updateCacheAndReturnNonEmpty();
323 : }
324 :
325 10 : bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
326 20 : AUTO_RASTERCLIP_VALIDATE(*this);
327 10 : clip.validate();
328 :
329 10 : if (this->isBW() && clip.isBW()) {
330 0 : (void)fBW.op(clip.fBW, op);
331 : } else {
332 20 : SkAAClip tmp;
333 : const SkAAClip* other;
334 :
335 10 : if (this->isBW()) {
336 0 : this->convertToAA();
337 : }
338 10 : if (clip.isBW()) {
339 0 : tmp.setRegion(clip.bwRgn());
340 0 : other = &tmp;
341 : } else {
342 10 : other = &clip.aaRgn();
343 : }
344 10 : (void)fAA.op(*other, op);
345 : }
346 20 : return this->updateCacheAndReturnNonEmpty();
347 : }
348 :
349 : /**
350 : * Our antialiasing currently has a granularity of 1/4 of a pixel along each
351 : * axis. Thus we can treat an axis coordinate as an integer if it differs
352 : * from its nearest int by < half of that value (1.8 in this case).
353 : */
354 1674 : static bool nearly_integral(SkScalar x) {
355 : static const SkScalar domain = SK_Scalar1 / 4;
356 : static const SkScalar halfDomain = domain / 2;
357 :
358 1674 : x += halfDomain;
359 1674 : return x - SkScalarFloorToScalar(x) < domain;
360 : }
361 :
362 532 : bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
363 : SkRegion::Op op, bool doAA) {
364 1064 : AUTO_RASTERCLIP_VALIDATE(*this);
365 : SkRect devRect;
366 :
367 532 : const bool isScaleTrans = matrix.isScaleTranslate();
368 532 : if (!isScaleTrans) {
369 0 : SkPath path;
370 0 : path.addRect(localRect);
371 0 : path.setIsVolatile(true);
372 0 : return this->op(path, matrix, devBounds, op, doAA);
373 : }
374 :
375 532 : matrix.mapRect(&devRect, localRect);
376 :
377 532 : if (fIsBW && doAA) {
378 : // check that the rect really needs aa, or is it close enought to
379 : // integer boundaries that we can just treat it as a BW rect?
380 1675 : if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
381 1255 : nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
382 418 : doAA = false;
383 : }
384 : }
385 :
386 532 : if (fIsBW && !doAA) {
387 : SkIRect ir;
388 419 : devRect.round(&ir);
389 419 : this->applyClipRestriction(op, &ir);
390 419 : (void)fBW.op(ir, op);
391 : } else {
392 113 : if (fIsBW) {
393 1 : this->convertToAA();
394 : }
395 113 : this->applyClipRestriction(op, &devRect);
396 113 : (void)fAA.op(devRect, op, doAA);
397 : }
398 532 : return this->updateCacheAndReturnNonEmpty();
399 : }
400 :
401 0 : void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
402 0 : if (nullptr == dst) {
403 0 : return;
404 : }
405 :
406 0 : AUTO_RASTERCLIP_VALIDATE(*this);
407 :
408 0 : if (this->isEmpty()) {
409 0 : dst->setEmpty();
410 0 : return;
411 : }
412 0 : if (0 == (dx | dy)) {
413 0 : *dst = *this;
414 0 : return;
415 : }
416 :
417 0 : dst->fIsBW = fIsBW;
418 0 : if (fIsBW) {
419 0 : fBW.translate(dx, dy, &dst->fBW);
420 0 : dst->fAA.setEmpty();
421 : } else {
422 0 : fAA.translate(dx, dy, &dst->fAA);
423 0 : dst->fBW.setEmpty();
424 : }
425 0 : dst->updateCacheAndReturnNonEmpty();
426 : }
427 :
428 43 : bool SkRasterClip::quickContains(const SkIRect& ir) const {
429 43 : return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
430 : }
431 :
432 : ///////////////////////////////////////////////////////////////////////////////
433 :
434 0 : const SkRegion& SkRasterClip::forceGetBW() {
435 0 : AUTO_RASTERCLIP_VALIDATE(*this);
436 :
437 0 : if (!fIsBW) {
438 0 : fBW.setRect(fAA.getBounds());
439 : }
440 0 : return fBW;
441 : }
442 :
443 40 : void SkRasterClip::convertToAA() {
444 80 : AUTO_RASTERCLIP_VALIDATE(*this);
445 :
446 40 : SkASSERT(fIsBW);
447 40 : fAA.setRegion(fBW);
448 40 : fIsBW = false;
449 :
450 : // since we are being explicitly asked to convert-to-aa, we pass false so we don't "optimize"
451 : // ourselves back to BW.
452 40 : (void)this->updateCacheAndReturnNonEmpty(false);
453 40 : }
454 :
455 : #ifdef SK_DEBUG
456 4674 : void SkRasterClip::validate() const {
457 : // can't ever assert that fBW is empty, since we may have called forceGetBW
458 4674 : if (fIsBW) {
459 3777 : SkASSERT(fAA.isEmpty());
460 : }
461 :
462 4674 : fBW.validate();
463 4674 : fAA.validate();
464 :
465 4674 : SkASSERT(this->computeIsEmpty() == fIsEmpty);
466 4674 : SkASSERT(this->computeIsRect() == fIsRect);
467 4674 : }
468 : #endif
469 :
470 : ///////////////////////////////////////////////////////////////////////////////
471 :
472 17 : SkAAClipBlitterWrapper::SkAAClipBlitterWrapper() {
473 17 : SkDEBUGCODE(fClipRgn = nullptr;)
474 17 : SkDEBUGCODE(fBlitter = nullptr;)
475 17 : }
476 :
477 169 : SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkRasterClip& clip,
478 169 : SkBlitter* blitter) {
479 169 : this->init(clip, blitter);
480 169 : }
481 :
482 0 : SkAAClipBlitterWrapper::SkAAClipBlitterWrapper(const SkAAClip* aaclip,
483 0 : SkBlitter* blitter) {
484 0 : SkASSERT(blitter);
485 0 : SkASSERT(aaclip);
486 0 : fBWRgn.setRect(aaclip->getBounds());
487 0 : fAABlitter.init(blitter, aaclip);
488 : // now our return values
489 0 : fClipRgn = &fBWRgn;
490 0 : fBlitter = &fAABlitter;
491 0 : }
492 :
493 171 : void SkAAClipBlitterWrapper::init(const SkRasterClip& clip, SkBlitter* blitter) {
494 171 : SkASSERT(blitter);
495 171 : if (clip.isBW()) {
496 14 : fClipRgn = &clip.bwRgn();
497 14 : fBlitter = blitter;
498 : } else {
499 157 : const SkAAClip& aaclip = clip.aaRgn();
500 157 : fBWRgn.setRect(aaclip.getBounds());
501 157 : fAABlitter.init(blitter, &aaclip);
502 : // now our return values
503 157 : fClipRgn = &fBWRgn;
504 157 : fBlitter = &fAABlitter;
505 : }
506 171 : }
|