Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrClip.h
Go to the documentation of this file.
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 GrClip_DEFINED
9#define GrClip_DEFINED
10
12#include "include/core/SkRect.h"
16
17class GrDrawOp;
19namespace skgpu {
20namespace ganesh {
22}
23} // namespace skgpu
24
25/**
26 * GrClip is an abstract base class for applying a clip. It constructs a clip mask if necessary, and
27 * fills out a GrAppliedClip instructing the caller on how to set up the draw state.
28 */
29class GrClip {
30public:
31 enum class Effect {
32 // The clip conservatively modifies the draw's coverage but doesn't eliminate the draw
34 // The clip definitely does not modify the draw's coverage and the draw can be performed
35 // without clipping (beyond the automatic device bounds clip).
37 // The clip definitely eliminates all of the draw's coverage and the draw can be skipped
39 };
40
43 SkRRect fRRect; // Ignore if 'isRRect' is false
44 GrAA fAA; // Ignore if 'isRRect' is false
46
47 PreClipResult(Effect effect) : fEffect(effect), fIsRRect(false) {}
48 PreClipResult(SkRect rect, GrAA aa) : PreClipResult(SkRRect::MakeRect(rect), aa) {}
51 , fRRect(rrect)
52 , fAA(aa)
53 , fIsRRect(true) {}
54 };
55
56 virtual ~GrClip() {}
57
58 /**
59 * Compute a conservative pixel bounds restricted to the given render target dimensions.
60 * The returned bounds represent the limits of pixels that can be drawn; anything outside of the
61 * bounds will be entirely clipped out.
62 */
63 virtual SkIRect getConservativeBounds() const = 0;
64
65 /**
66 * This computes a GrAppliedClip from the clip which in turn can be used to build a GrPipeline.
67 * To determine the appropriate clipping implementation the GrClip subclass must know whether
68 * the draw will enable HW AA or uses the stencil buffer. On input 'bounds' is a conservative
69 * bounds of the draw that is to be clipped. If kClipped or kUnclipped is returned, the 'bounds'
70 * will have been updated to be contained within the clip bounds (or the device's, for wide-open
71 * clips). If kNoDraw is returned, 'bounds' and the applied clip are in an undetermined state
72 * and should be ignored (and the draw should be skipped).
73 */
76 GrDrawOp*,
79 SkRect* bounds) const = 0;
80
81 /**
82 * Perform preliminary, conservative analysis on the draw bounds as if it were provided to
83 * apply(). The results of this are returned the PreClipResults struct, where 'result.fEffect'
84 * corresponds to what 'apply' would return. If this value is kUnclipped or kNoDraw, then it
85 * can be assumed that apply() would also always result in the same Effect.
86 *
87 * If kClipped is returned, apply() may further refine the effect to kUnclipped or kNoDraw,
88 * with one exception. When 'result.fIsRRect' is true, preApply() reports the single round rect
89 * and anti-aliased state that would act as an intersection on the draw geometry. If no further
90 * action is taken to modify the draw, apply() will represent this round rect in the applied
91 * clip.
92 *
93 * When set, 'result.fRRect' will intersect with the render target bounds but may extend
94 * beyond it. If the render target bounds are the only clip effect on the draw, this is reported
95 * as kUnclipped and not as a degenerate rrect that matches the bounds.
96 */
97 virtual PreClipResult preApply(const SkRect& drawBounds, GrAA aa) const {
98 SkIRect pixelBounds = GetPixelIBounds(drawBounds, aa);
99 bool outside = !SkIRect::Intersects(pixelBounds, this->getConservativeBounds());
100 return outside ? Effect::kClippedOut : Effect::kClipped;
101 }
102
103 /**
104 * This is the maximum distance that a draw may extend beyond a clip's boundary and still count
105 * count as "on the other side". We leave some slack because floating point rounding error is
106 * likely to blame. The rationale for 1e-3 is that in the coverage case (and barring unexpected
107 * rounding), as long as coverage stays within 0.5 * 1/256 of its intended value it shouldn't
108 * have any effect on the final pixel values.
109 */
110 constexpr static SkScalar kBoundsTolerance = 1e-3f;
111
112 /**
113 * This is the slack around a half-pixel vertex coordinate where we don't trust the GPU's
114 * rasterizer to round consistently. The rounding method is not defined in GPU specs, and
115 * rasterizer precision frequently introduces errors where a fraction < 1/2 still rounds up.
116 *
117 * For non-AA bounds edges, an edge value between 0.45 and 0.55 will round in or round out
118 * depending on what side its on. Outside of this range, the non-AA edge will snap using round()
119 */
120 constexpr static SkScalar kHalfPixelRoundingTolerance = 5e-2f;
121
122 /**
123 * Returns true if the given draw bounds count as entirely inside the clip.
124
125 * @param innerClipBounds device-space rect fully contained by the clip
126 * @param drawBounds device-space bounds of the query region.
127 */
128 static bool IsInsideClip(const SkIRect& innerClipBounds, const SkRect& drawBounds, GrAA aa) {
129 return innerClipBounds.contains(GetPixelIBounds(drawBounds, aa));
130 }
131
132 /**
133 * Returns true if the given draw bounds count as entirely outside the clip.
134
135 * @param outerClipBounds device-space rect that contains the clip
136 * @param drawBounds device-space bounds of the query region.
137 * @param aa whether or not the draw will use anti-aliasing
138 */
139 static bool IsOutsideClip(const SkIRect& outerClipBounds, const SkRect& drawBounds, GrAA aa) {
140 return !SkIRect::Intersects(outerClipBounds, GetPixelIBounds(drawBounds, aa));
141 }
142
143 // Modifies the behavior of GetPixelIBounds
144 enum class BoundsType {
145 /**
146 * Returns the tightest integer pixel bounding box such that the rasterization of a shape
147 * contained in the analytic 'bounds', using the 'aa' method, will only have non-zero
148 * coverage for pixels inside the returned bounds. Pixels outside the bounds will either
149 * not be touched, or will have 0 coverage that creates no visual change.
150 */
151 kExterior,
152 /**
153 * Returns the largest integer pixel bounding box such that were 'bounds' to be rendered as
154 * a solid fill using 'aa', every pixel in the returned bounds will have full coverage.
155 *
156 * This effectively determines the pixels that are definitely covered by a draw or clip. It
157 * effectively performs the opposite operations as GetOuterPixelBounds. It rounds in instead
158 * of out for coverage AA and non-AA near pixel centers.
159 */
161 };
162
163 /**
164 * Convert the analytic bounds of a shape into an integer pixel bounds, where the given aa type
165 * is used when the shape is rendered. The bounds mode can be used to query exterior or interior
166 * pixel boundaries. Interior bounds only make sense when its know that the analytic bounds
167 * are filled completely.
168 *
169 * NOTE: When using kExterior_Bounds, some coverage-AA rendering methods may still touch a pixel
170 * center outside of these bounds but will evaluate to 0 coverage. This is visually acceptable,
171 * but an additional outset of 1px should be used for dst proxy access.
172 */
173 static SkIRect GetPixelIBounds(const SkRect& bounds, GrAA aa,
175 auto roundLow = [aa](float v) {
176 v += kBoundsTolerance;
179 };
180 auto roundHigh = [aa](float v) {
181 v -= kBoundsTolerance;
184 };
185
186 if (bounds.isEmpty()) {
187 return SkIRect::MakeEmpty();
188 }
189
190 if (mode == BoundsType::kExterior) {
191 return SkIRect::MakeLTRB(roundLow(bounds.fLeft), roundLow(bounds.fTop),
192 roundHigh(bounds.fRight), roundHigh(bounds.fBottom));
193 } else {
194 return SkIRect::MakeLTRB(roundHigh(bounds.fLeft), roundHigh(bounds.fTop),
195 roundLow(bounds.fRight), roundLow(bounds.fBottom));
196 }
197 }
198
199 /**
200 * Returns true if the given rect counts as aligned with pixel boundaries.
201 */
202 static bool IsPixelAligned(const SkRect& rect) {
203 return SkScalarAbs(SkScalarRoundToScalar(rect.fLeft) - rect.fLeft) <= kBoundsTolerance &&
204 SkScalarAbs(SkScalarRoundToScalar(rect.fTop) - rect.fTop) <= kBoundsTolerance &&
205 SkScalarAbs(SkScalarRoundToScalar(rect.fRight) - rect.fRight) <= kBoundsTolerance &&
206 SkScalarAbs(SkScalarRoundToScalar(rect.fBottom) - rect.fBottom) <= kBoundsTolerance;
207 }
208};
209
210
211/**
212 * GrHardClip never uses coverage FPs. It can only enforce the clip using the already-existing
213 * stencil buffer contents and/or fixed-function state like scissor. Always aliased if MSAA is off.
214 */
215class GrHardClip : public GrClip {
216public:
217 /**
218 * Sets the appropriate hardware state modifications on GrAppliedHardClip that will implement
219 * the clip. On input 'bounds' is a conservative bounds of the draw that is to be clipped. After
220 * return 'bounds' has been intersected with a conservative bounds of the clip.
221 */
222 virtual Effect apply(GrAppliedHardClip* out, SkIRect* bounds) const = 0;
223
224private:
227 GrDrawOp*,
228 GrAAType aa,
229 GrAppliedClip* out,
230 SkRect* bounds) const final {
231 SkIRect pixelBounds = GetPixelIBounds(*bounds, GrAA(aa != GrAAType::kNone));
232 Effect effect = this->apply(&out->hardClip(), &pixelBounds);
233 bounds->intersect(SkRect::Make(pixelBounds));
234 return effect;
235 }
236};
237
238#endif
GrAAType
GrAA
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
#define SkScalarRoundToScalar(x)
Definition SkScalar.h:32
#define SkScalarFloorToInt(x)
Definition SkScalar.h:35
#define SkScalarAbs(x)
Definition SkScalar.h:39
virtual Effect apply(GrRecordingContext *, skgpu::ganesh::SurfaceDrawContext *, GrDrawOp *, GrAAType, GrAppliedClip *, SkRect *bounds) const =0
virtual ~GrClip()
Definition GrClip.h:56
static bool IsPixelAligned(const SkRect &rect)
Definition GrClip.h:202
static SkIRect GetPixelIBounds(const SkRect &bounds, GrAA aa, BoundsType mode=BoundsType::kExterior)
Definition GrClip.h:173
BoundsType
Definition GrClip.h:144
static bool IsOutsideClip(const SkIRect &outerClipBounds, const SkRect &drawBounds, GrAA aa)
Definition GrClip.h:139
static constexpr SkScalar kBoundsTolerance
Definition GrClip.h:110
virtual SkIRect getConservativeBounds() const =0
static bool IsInsideClip(const SkIRect &innerClipBounds, const SkRect &drawBounds, GrAA aa)
Definition GrClip.h:128
virtual PreClipResult preApply(const SkRect &drawBounds, GrAA aa) const
Definition GrClip.h:97
static constexpr SkScalar kHalfPixelRoundingTolerance
Definition GrClip.h:120
Effect apply(GrRecordingContext *, skgpu::ganesh::SurfaceDrawContext *, GrDrawOp *, GrAAType aa, GrAppliedClip *out, SkRect *bounds) const final
Definition GrClip.h:225
virtual Effect apply(GrAppliedHardClip *out, SkIRect *bounds) const =0
float SkScalar
Definition extension.cpp:12
PreClipResult(Effect effect)
Definition GrClip.h:47
PreClipResult(SkRect rect, GrAA aa)
Definition GrClip.h:48
PreClipResult(SkRRect rrect, GrAA aa)
Definition GrClip.h:49
static bool Intersects(const SkIRect &a, const SkIRect &b)
Definition SkRect.h:535
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition SkRect.h:91
static constexpr SkIRect MakeEmpty()
Definition SkRect.h:45
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
static SkRect Make(const SkISize &size)
Definition SkRect.h:669