| 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 | |
| 16 | class SkRRect; |
| 17 | |
| 18 | class 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 | |
| 31 | public: |
| 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 | */ |
| 60 | class SkRasterClip { |
| 61 | public: |
| 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 | |
| 138 | private: |
| 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 | |
| 197 | class SkAutoRasterClipValidate : SkNoncopyable { |
| 198 | public: |
| 199 | SkAutoRasterClipValidate(const SkRasterClip& rc) : fRC(rc) { |
| 200 | fRC.validate(); |
| 201 | } |
| 202 | ~SkAutoRasterClipValidate() { |
| 203 | fRC.validate(); |
| 204 | } |
| 205 | private: |
| 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 | */ |
| 227 | class SkAAClipBlitterWrapper { |
| 228 | public: |
| 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 | |
| 248 | private: |
| 249 | SkRegion fBWRgn; |
| 250 | SkAAClipBlitter fAABlitter; |
| 251 | // what we return |
| 252 | const SkRegion* fClipRgn; |
| 253 | SkBlitter* fBlitter; |
| 254 | }; |
| 255 | |
| 256 | #endif |
| 257 | |