1 /*
2 * Copyright 2017 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 "src/core/SkClipStackDevice.h"
9
10 #include "include/core/SkImageInfo.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkRegion.h"
15 #include "include/core/SkShader.h"
16 #include "include/private/base/SkAssert.h"
17 #include "src/core/SkMatrixPriv.h"
18
19 #include <utility>
20
21 class SkRRect;
22 enum class SkClipOp;
23
devClipBounds() const24 SkIRect SkClipStackDevice::devClipBounds() const {
25 SkIRect r = fClipStack.bounds(this->imageInfo().bounds()).roundOut();
26 if (!r.isEmpty()) {
27 SkASSERT(this->imageInfo().bounds().contains(r));
28 }
29 return r;
30 }
31
32 ///////////////////////////////////////////////////////////////////////////////////////////////////
33
pushClipStack()34 void SkClipStackDevice::pushClipStack() {
35 fClipStack.save();
36 }
37
popClipStack()38 void SkClipStackDevice::popClipStack() {
39 fClipStack.restore();
40 }
41
clipRect(const SkRect & rect,SkClipOp op,bool aa)42 void SkClipStackDevice::clipRect(const SkRect& rect, SkClipOp op, bool aa) {
43 fClipStack.clipRect(rect, this->localToDevice(), op, aa);
44 }
45
clipRRect(const SkRRect & rrect,SkClipOp op,bool aa)46 void SkClipStackDevice::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
47 fClipStack.clipRRect(rrect, this->localToDevice(), op, aa);
48 }
49
clipPath(const SkPath & path,SkClipOp op,bool aa)50 void SkClipStackDevice::clipPath(const SkPath& path, SkClipOp op, bool aa) {
51 fClipStack.clipPath(path, this->localToDevice(), op, aa);
52 }
53
onClipShader(sk_sp<SkShader> shader)54 void SkClipStackDevice::onClipShader(sk_sp<SkShader> shader) {
55 fClipStack.clipShader(std::move(shader));
56 }
57
clipRegion(const SkRegion & rgn,SkClipOp op)58 void SkClipStackDevice::clipRegion(const SkRegion& rgn, SkClipOp op) {
59 SkIPoint origin = this->getOrigin();
60 SkRegion tmp;
61 SkPath path;
62 rgn.getBoundaryPath(&path);
63 path.transform(SkMatrix::Translate(-origin));
64 fClipStack.clipPath(path, SkMatrix::I(), op, false);
65 }
66
replaceClip(const SkIRect & rect)67 void SkClipStackDevice::replaceClip(const SkIRect& rect) {
68 SkRect deviceRect = SkMatrixPriv::MapRect(this->globalToDevice(), SkRect::Make(rect));
69 fClipStack.replaceClip(deviceRect, /*doAA=*/false);
70 }
71
isClipAntiAliased() const72 bool SkClipStackDevice::isClipAntiAliased() const {
73 SkClipStack::B2TIter iter(fClipStack);
74 const SkClipStack::Element* element;
75
76 while ((element = iter.next()) != nullptr) {
77 if (element->isAA()) {
78 return true;
79 }
80 }
81 return false;
82 }
83
isClipWideOpen() const84 bool SkClipStackDevice::isClipWideOpen() const {
85 return fClipStack.quickContains(SkRect::MakeIWH(this->width(), this->height()));
86 }
87
isClipEmpty() const88 bool SkClipStackDevice::isClipEmpty() const {
89 return fClipStack.isEmpty(SkIRect::MakeWH(this->width(), this->height()));
90 }
91
isClipRect() const92 bool SkClipStackDevice::isClipRect() const {
93 if (this->isClipWideOpen()) {
94 return true;
95 } else if (this->isClipEmpty()) {
96 return false;
97 }
98
99 SkClipStack::BoundsType boundType;
100 bool isIntersectionOfRects;
101 SkRect bounds;
102 fClipStack.getBounds(&bounds, &boundType, &isIntersectionOfRects);
103 return isIntersectionOfRects && boundType == SkClipStack::kNormal_BoundsType;
104 }
105
android_utils_clipAsRgn(SkRegion * rgn) const106 void SkClipStackDevice::android_utils_clipAsRgn(SkRegion* rgn) const {
107 SkClipStack::BoundsType boundType;
108 bool isIntersectionOfRects;
109 SkRect bounds;
110 fClipStack.getBounds(&bounds, &boundType, &isIntersectionOfRects);
111 if (isIntersectionOfRects && SkClipStack::kNormal_BoundsType == boundType) {
112 rgn->setRect(bounds.round());
113 } else {
114 SkRegion boundsRgn({0, 0, this->width(), this->height()});
115 SkPath tmpPath;
116
117 *rgn = boundsRgn;
118 SkClipStack::B2TIter iter(fClipStack);
119 while (auto elem = iter.next()) {
120 tmpPath.rewind();
121 elem->asDeviceSpacePath(&tmpPath);
122 SkRegion tmpRgn;
123 tmpRgn.setPath(tmpPath, boundsRgn);
124 if (elem->isReplaceOp()) {
125 // All replace elements are rectangles
126 // TODO: SkClipStack can be simplified to be I,D,R ops now, which means element
127 // iteration can be from top of the stack to the most recent replace element.
128 // When that's done, this loop will be simplifiable.
129 rgn->setRect(elem->getDeviceSpaceRect().round());
130 } else {
131 rgn->op(tmpRgn, static_cast<SkRegion::Op>(elem->getOp()));
132 }
133 }
134 }
135 }
136