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#ifndef SkRasterClip_DEFINED
9#define SkRasterClip_DEFINED
10
11#include "include/core/SkRegion.h"
12#include "include/core/SkShader.h"
13#include "include/private/SkMacros.h"
14#include "src/core/SkAAClip.h"
15
16class SkRRect;
17
18class SkConservativeClip {
19 SkIRect fBounds;
20 const SkIRect* fClipRestrictionRect;
21
22 inline void applyClipRestriction(SkRegion::Op op, SkIRect* bounds) {
23 if (op >= SkRegion::kUnion_Op && fClipRestrictionRect
24 && !fClipRestrictionRect->isEmpty()) {
25 if (!bounds->intersect(*fClipRestrictionRect)) {
26 bounds->setEmpty();
27 }
28 }
29 }
30
31public:
32 SkConservativeClip() : fBounds(SkIRect::MakeEmpty()), fClipRestrictionRect(nullptr) {}
33
34 bool isEmpty() const { return fBounds.isEmpty(); }
35 bool isRect() const { return true; }
36 const SkIRect& getBounds() const { return fBounds; }
37
38 void setEmpty() { fBounds.setEmpty(); }
39 void setRect(const SkIRect& r) { fBounds = r; }
40 void setDeviceClipRestriction(const SkIRect* rect) {
41 fClipRestrictionRect = rect;
42 }
43
44 void opRect(const SkRect&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA);
45 void opRRect(const SkRRect&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA);
46 void opPath(const SkPath&, const SkMatrix&, const SkIRect& limit, SkRegion::Op, bool isAA);
47 void opRegion(const SkRegion&, SkRegion::Op);
48 void opIRect(const SkIRect&, SkRegion::Op);
49};
50
51/**
52 * Wraps a SkRegion and SkAAClip, so we have a single object that can represent either our
53 * BW or antialiased clips.
54 *
55 * This class is optimized for the raster backend of canvas, but can be expense to keep up2date,
56 * so it supports a runtime option (force-conservative-rects) to turn it into a super-fast
57 * rect-only tracker. The gpu backend uses this since it does not need the result (it uses
58 * SkClipStack instead).
59 */
60class SkRasterClip {
61public:
62 SkRasterClip();
63 SkRasterClip(const SkIRect&);
64 SkRasterClip(const SkRegion&);
65 SkRasterClip(const SkRasterClip&);
66 SkRasterClip& operator=(const SkRasterClip&);
67 ~SkRasterClip();
68
69 // Only compares the current state. Does not compare isForceConservativeRects(), so that field
70 // could be different but this could still return true.
71 bool operator==(const SkRasterClip&) const;
72 bool operator!=(const SkRasterClip& other) const {
73 return !(*this == other);
74 }
75
76 bool isBW() const { return fIsBW; }
77 bool isAA() const { return !fIsBW; }
78 const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; }
79 const SkAAClip& aaRgn() const { SkASSERT(!fIsBW); return fAA; }
80
81 bool isEmpty() const {
82 SkASSERT(this->computeIsEmpty() == fIsEmpty);
83 return fIsEmpty;
84 }
85
86 bool isRect() const {
87 SkASSERT(this->computeIsRect() == fIsRect);
88 return fIsRect;
89 }
90
91 bool isComplex() const;
92 const SkIRect& getBounds() const;
93
94 bool setEmpty();
95 bool setRect(const SkIRect&);
96
97 bool op(const SkIRect&, SkRegion::Op);
98 bool op(const SkRegion&, SkRegion::Op);
99 bool op(const SkRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
100 bool op(const SkRRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
101 bool op(const SkPath&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
102 bool op(sk_sp<SkShader>);
103
104 void translate(int dx, int dy, SkRasterClip* dst) const;
105 void translate(int dx, int dy) {
106 this->translate(dx, dy, this);
107 }
108
109 bool quickContains(const SkIRect& rect) const;
110 bool quickContains(int left, int top, int right, int bottom) const {
111 return quickContains(SkIRect::MakeLTRB(left, top, right, bottom));
112 }
113
114 /**
115 * Return true if this region is empty, or if the specified rectangle does
116 * not intersect the region. Returning false is not a guarantee that they
117 * intersect, but returning true is a guarantee that they do not.
118 */
119 bool quickReject(const SkIRect& rect) const {
120 return !SkIRect::Intersects(this->getBounds(), rect);
121 }
122
123 // hack for SkCanvas::getTotalClip
124 const SkRegion& forceGetBW();
125
126#ifdef SK_DEBUG
127 void validate() const;
128#else
129 void validate() const {}
130#endif
131
132 void setDeviceClipRestriction(const SkIRect* rect) {
133 fClipRestrictionRect = rect;
134 }
135
136 sk_sp<SkShader> clipShader() const { return fShader; }
137
138private:
139 SkRegion fBW;
140 SkAAClip fAA;
141 bool fIsBW;
142 // these 2 are caches based on querying the right obj based on fIsBW
143 bool fIsEmpty;
144 bool fIsRect;
145 const SkIRect* fClipRestrictionRect = nullptr;
146 // if present, this augments the clip, not replaces it
147 sk_sp<SkShader> fShader;
148
149 bool computeIsEmpty() const {
150 return fIsBW ? fBW.isEmpty() : fAA.isEmpty();
151 }
152
153 bool computeIsRect() const {
154 return fIsBW ? fBW.isRect() : fAA.isRect();
155 }
156
157 bool updateCacheAndReturnNonEmpty(bool detectAARect = true) {
158 fIsEmpty = this->computeIsEmpty();
159
160 // detect that our computed AA is really just a (hard-edged) rect
161 if (detectAARect && !fIsEmpty && !fIsBW && fAA.isRect()) {
162 fBW.setRect(fAA.getBounds());
163 fAA.setEmpty(); // don't need this guy anymore
164 fIsBW = true;
165 }
166
167 fIsRect = this->computeIsRect();
168 return !fIsEmpty;
169 }
170
171 void convertToAA();
172
173 bool setPath(const SkPath& path, const SkRegion& clip, bool doAA);
174 bool setPath(const SkPath& path, const SkIRect& clip, bool doAA);
175 bool op(const SkRasterClip&, SkRegion::Op);
176 bool setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse);
177
178 inline void applyClipRestriction(SkRegion::Op op, SkIRect* bounds) {
179 if (op >= SkRegion::kUnion_Op && fClipRestrictionRect
180 && !fClipRestrictionRect->isEmpty()) {
181 if (!bounds->intersect(*fClipRestrictionRect)) {
182 bounds->setEmpty();
183 }
184 }
185 }
186
187 inline void applyClipRestriction(SkRegion::Op op, SkRect* bounds) {
188 if (op >= SkRegion::kUnion_Op && fClipRestrictionRect
189 && !fClipRestrictionRect->isEmpty()) {
190 if (!bounds->intersect(SkRect::Make(*fClipRestrictionRect))) {
191 bounds->setEmpty();
192 }
193 }
194 }
195};
196
197class SkAutoRasterClipValidate : SkNoncopyable {
198public:
199 SkAutoRasterClipValidate(const SkRasterClip& rc) : fRC(rc) {
200 fRC.validate();
201 }
202 ~SkAutoRasterClipValidate() {
203 fRC.validate();
204 }
205private:
206 const SkRasterClip& fRC;
207};
208#define SkAutoRasterClipValidate(...) SK_REQUIRE_LOCAL_VAR(SkAutoRasterClipValidate)
209
210#ifdef SK_DEBUG
211 #define AUTO_RASTERCLIP_VALIDATE(rc) SkAutoRasterClipValidate arcv(rc)
212#else
213 #define AUTO_RASTERCLIP_VALIDATE(rc)
214#endif
215
216///////////////////////////////////////////////////////////////////////////////
217
218/**
219 * Encapsulates the logic of deciding if we need to change/wrap the blitter
220 * for aaclipping. If so, getRgn and getBlitter return modified values. If
221 * not, they return the raw blitter and (bw) clip region.
222 *
223 * We need to keep the constructor/destructor cost as small as possible, so we
224 * can freely put this guy on the stack, and not pay too much for the case when
225 * we're really BW anyways.
226 */
227class SkAAClipBlitterWrapper {
228public:
229 SkAAClipBlitterWrapper();
230 SkAAClipBlitterWrapper(const SkRasterClip&, SkBlitter*);
231 SkAAClipBlitterWrapper(const SkAAClip*, SkBlitter*);
232
233 void init(const SkRasterClip&, SkBlitter*);
234
235 const SkIRect& getBounds() const {
236 SkASSERT(fClipRgn);
237 return fClipRgn->getBounds();
238 }
239 const SkRegion& getRgn() const {
240 SkASSERT(fClipRgn);
241 return *fClipRgn;
242 }
243 SkBlitter* getBlitter() {
244 SkASSERT(fBlitter);
245 return fBlitter;
246 }
247
248private:
249 SkRegion fBWRgn;
250 SkAAClipBlitter fAABlitter;
251 // what we return
252 const SkRegion* fClipRgn;
253 SkBlitter* fBlitter;
254};
255
256#endif
257