OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2016 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 "gm.h" | |
9 #include "SkClipStack.h" | |
10 #include "SkRRect.h" | |
11 | |
12 #if SK_SUPPORT_GPU | |
13 # include "GrAppliedClip.h" | |
14 # include "GrDrawContext.h" | |
15 # include "GrDrawContextPriv.h" | |
16 # include "GrFixedClip.h" | |
17 # include "GrReducedClip.h" | |
18 # include "GrRenderTargetPriv.h" | |
19 # include "GrResourceProvider.h" | |
20 # include "effects/GrTextureDomain.h" | |
21 #endif | |
22 | |
23 constexpr static SkIRect kDeviceRect = {0, 0, 600, 600}; | |
24 constexpr static SkIRect kLayerRect = {25, 25, 575, 575}; | |
25 constexpr static SkIRect kCoverRect = {50, 50, 550, 550}; | |
26 constexpr static int kNumWindows = 8; | |
27 | |
28 namespace skiagm { | |
29 | |
30 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
31 | |
32 class WindowRectanglesBaseGM : public GM { | |
33 protected: | |
34 virtual void onCoverClipStack(const SkClipStack&, SkCanvas*) = 0; | |
35 | |
36 private: | |
37 SkISize onISize() override { return SkISize::Make(kDeviceRect.width(), kDevi
ceRect.height()); } | |
38 void onDraw(SkCanvas*) final; | |
39 }; | |
40 | |
41 void WindowRectanglesBaseGM::onDraw(SkCanvas* canvas) { | |
42 sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 25); | |
43 canvas->saveLayer(SkRect::Make(kLayerRect), nullptr); | |
44 | |
45 SkClipStack stack; | |
46 stack.clipDevRect(SkRect::MakeXYWH(370.75, 80.25, 149, 100), SkRegion::kDiff
erence_Op, false); | |
47 stack.clipDevRect(SkRect::MakeXYWH(80.25, 420.75, 150, 100), SkRegion::kDiff
erence_Op, true); | |
48 stack.clipDevRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(200, 200, 200, 200),
60, 45), | |
49 SkRegion::kDifference_Op, true); | |
50 | |
51 SkRRect nine; | |
52 nine.setNinePatch(SkRect::MakeXYWH(550 - 30.25 - 100, 370.75, 100, 150), 12,
35, 23, 20); | |
53 stack.clipDevRRect(nine, SkRegion::kDifference_Op, true); | |
54 | |
55 SkRRect complx; | |
56 SkVector complxRadii[4] = {{6, 4}, {8, 12}, {16, 24}, {48, 32}}; | |
57 complx.setRectRadii(SkRect::MakeXYWH(80.25, 80.75, 100, 149), complxRadii); | |
58 stack.clipDevRRect(complx, SkRegion::kDifference_Op, false); | |
59 | |
60 this->onCoverClipStack(stack, canvas); | |
61 | |
62 canvas->restore(); | |
63 } | |
64 | |
65 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
66 | |
67 /** | |
68 * Draws a clip that will exercise window rectangles if they are supported. | |
69 */ | |
70 class WindowRectanglesGM : public WindowRectanglesBaseGM { | |
71 private: | |
72 SkString onShortName() final { return SkString("windowrectangles"); } | |
73 void onCoverClipStack(const SkClipStack&, SkCanvas*) final; | |
74 }; | |
75 | |
76 /** | |
77 * This is a simple helper class for resetting a canvas's clip to our test’s SkC
lipStack. | |
78 */ | |
79 class ReplayClipStackVisitor final : public SkCanvasClipVisitor { | |
80 public: | |
81 typedef SkRegion::Op Op; | |
82 ReplayClipStackVisitor(SkCanvas* canvas) : fCanvas(canvas) {} | |
83 void clipRect(const SkRect& r, Op op, bool aa) override { fCanvas->clipRect(
r, op, aa); } | |
84 void clipRRect(const SkRRect& rr, Op op, bool aa) override { fCanvas->clipRR
ect(rr, op, aa); } | |
85 void clipPath(const SkPath&, Op, bool) override { SkFAIL("Not implemented");
} | |
86 private: | |
87 SkCanvas* const fCanvas; | |
88 }; | |
89 | |
90 void WindowRectanglesGM::onCoverClipStack(const SkClipStack& stack, SkCanvas* ca
nvas) { | |
91 SkPaint paint; | |
92 paint.setColor(0xff00aa80); | |
93 | |
94 // Set up the canvas's clip to match our SkClipStack. | |
95 ReplayClipStackVisitor visitor(canvas); | |
96 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); | |
97 for (const SkClipStack::Element* element = iter.next(); element; element = i
ter.next()) { | |
98 element->replay(&visitor); | |
99 } | |
100 | |
101 canvas->drawRect(SkRect::Make(kCoverRect), paint); | |
102 } | |
103 | |
104 DEF_GM( return new WindowRectanglesGM(); ) | |
105 | |
106 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
107 | |
108 #if SK_SUPPORT_GPU | |
109 | |
110 /** | |
111 * Visualizes the mask (alpha or stencil) for a clip with several window rectang
les. The purpose of | |
112 * this test is to verify that window rectangles are being used during clip mask
generation, and to | |
113 * visualize where the window rectangles are placed. | |
114 * | |
115 * We use window rectangles when generating the clip mask because there is no ne
ed to invest time | |
116 * defining those regions where window rectangles will be in effect during the a
ctual draw anyway. | |
117 * | |
118 * This test works by filling the entire clip mask with a small checkerboard pat
tern before drawing | |
119 * it, and then covering the mask with a solid color once it has been generated.
The regions inside | |
120 * window rectangles or outside the scissor should still have the initial checke
rboard intact. | |
121 */ | |
122 class WindowRectanglesMaskGM : public WindowRectanglesBaseGM { | |
123 private: | |
124 constexpr static int kMaskCheckerSize = 5; | |
125 SkString onShortName() final { return SkString("windowrectangles_mask"); } | |
126 void onCoverClipStack(const SkClipStack&, SkCanvas*) final; | |
127 void visualizeAlphaMask(GrContext*, GrDrawContext*, const GrReducedClip&, co
nst GrPaint&); | |
128 void visualizeStencilMask(GrContext*, GrDrawContext*, const GrReducedClip&,
const GrPaint&); | |
129 void stencilCheckerboard(GrDrawContext*, bool flip); | |
130 void fail(SkCanvas*); | |
131 }; | |
132 | |
133 /** | |
134 * Base class for GrClips that visualize a clip mask. | |
135 */ | |
136 class MaskOnlyClipBase : public GrClip { | |
137 private: | |
138 bool quickContains(const SkRect&) const final { return false; } | |
139 bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final { re
turn false; } | |
140 void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior)
const final { | |
141 rect->set(0, 0, width, height); | |
142 if (iior) { | |
143 *iior = false; | |
144 } | |
145 } | |
146 }; | |
147 | |
148 /** | |
149 * This class clips a cover by an alpha mask. We use it to visualize the alpha c
lip mask. | |
150 */ | |
151 class AlphaOnlyClip final : public MaskOnlyClipBase { | |
152 public: | |
153 AlphaOnlyClip(GrTexture* mask, int x, int y) { | |
154 int w = mask->width(), h = mask->height(); | |
155 SkMatrix mat = SkMatrix::MakeScale(1.f / SkIntToScalar(w), 1.f / SkIntTo
Scalar(h)); | |
156 mat.preTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); | |
157 fFP = GrTextureDomainEffect::Make( | |
158 mask, nullptr, mat, | |
159 GrTextureDomain::MakeTexelDomain(mask, SkIRect::MakeWH(w, h)), | |
160 GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMod
e, | |
161 kDevice_GrCoordSet); | |
162 | |
163 } | |
164 private: | |
165 bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const
override { | |
166 out->addCoverageFP(fFP); | |
167 return true; | |
168 } | |
169 sk_sp<GrFragmentProcessor> fFP; | |
170 }; | |
171 | |
172 /** | |
173 * This class clips a cover by the stencil clip bit. We use it to visualize the
stencil mask. | |
174 */ | |
175 class StencilOnlyClip final : public MaskOnlyClipBase { | |
176 private: | |
177 bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const
override { | |
178 out->addStencilClip(); | |
179 return true; | |
180 } | |
181 }; | |
182 | |
183 void WindowRectanglesMaskGM::onCoverClipStack(const SkClipStack& stack, SkCanvas
* canvas) { | |
184 GrContext* ctx = canvas->getGrContext(); | |
185 GrDrawContext* dc = canvas->internal_private_accessTopLayerDrawContext(); | |
186 | |
187 if (!ctx || !dc || | |
188 dc->accessRenderTarget()->renderTargetPriv().maxWindowRectangles() < kNu
mWindows) { | |
189 this->fail(canvas); | |
190 return; | |
191 } | |
192 | |
193 const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), kNumWindows
); | |
194 | |
195 GrPaint paint; | |
196 paint.setAntiAlias(true); | |
197 if (!dc->isStencilBufferMultisampled()) { | |
198 paint.setColor4f(GrColor4f(0, 0.25f, 1, 1)); | |
199 this->visualizeAlphaMask(ctx, dc, reducedClip, paint); | |
200 } else { | |
201 paint.setColor4f(GrColor4f(1, 0.25f, 0.25f, 1)); | |
202 this->visualizeStencilMask(ctx, dc, reducedClip, paint); | |
203 } | |
204 } | |
205 | |
206 void WindowRectanglesMaskGM::visualizeAlphaMask(GrContext* ctx, GrDrawContext* d
c, | |
207 const GrReducedClip& reducedClip
, | |
208 const GrPaint& paint) { | |
209 GrPixelConfig config = kRGBA_8888_GrPixelConfig; | |
210 if (ctx->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { | |
211 config = kAlpha_8_GrPixelConfig; | |
212 } | |
213 | |
214 sk_sp<GrDrawContext> maskDC(ctx->makeDrawContext(SkBackingFit::kExact, kLaye
rRect.width(), | |
215 kLayerRect.height(), config
, nullptr)); | |
216 if (!maskDC || | |
217 !ctx->resourceProvider()->attachStencilAttachment(maskDC->accessRenderTa
rget())) { | |
218 return; | |
219 } | |
220 | |
221 // Draw a checker pattern into the alpha mask so we can visualize the region
s left untouched by | |
222 // the clip mask generation. | |
223 this->stencilCheckerboard(maskDC.get(), true); | |
224 maskDC->clear(nullptr, GrColorPackA4(0xff), true); | |
225 maskDC->drawContextPriv().drawAndStencilRect(StencilOnlyClip(), &GrUserStenc
ilSettings::kUnused, | |
226 SkRegion::kDifference_Op, false
, false, | |
227 SkMatrix::I(), | |
228 SkRect::MakeIWH(maskDC->width()
, maskDC->height())); | |
229 reducedClip.drawAlphaClipMask(maskDC.get()); | |
230 sk_sp<GrTexture> mask(maskDC->asTexture()); | |
231 | |
232 int x = kCoverRect.x() - kLayerRect.x(), | |
233 y = kCoverRect.y() - kLayerRect.y(); | |
234 | |
235 // Now visualize the alpha mask by drawing a rect over the area where it is
defined. The regions | |
236 // inside window rectangles or outside the scissor should still have the ini
tial checkerboard | |
237 // intact. (This verifies we didn't spend any time modifying those pixels in
the mask.) | |
238 AlphaOnlyClip clip(mask.get(), x, y); | |
239 dc->drawRect(clip, paint, SkMatrix::I(), | |
240 SkRect::Make(SkIRect::MakeXYWH(x, y, mask->width(), mask->heigh
t()))); | |
241 } | |
242 | |
243 void WindowRectanglesMaskGM::visualizeStencilMask(GrContext* ctx, GrDrawContext*
dc, | |
244 const GrReducedClip& reducedCl
ip, | |
245 const GrPaint& paint) { | |
246 if (!ctx->resourceProvider()->attachStencilAttachment(dc->accessRenderTarget
())) { | |
247 return; | |
248 } | |
249 | |
250 // Draw a checker pattern into the stencil buffer so we can visualize the re
gions left untouched | |
251 // by the clip mask generation. | |
252 this->stencilCheckerboard(dc, false); | |
253 reducedClip.drawStencilClipMask(ctx, dc, {kLayerRect.x(), kLayerRect.y()}); | |
254 | |
255 // Now visualize the stencil mask by covering the entire render target. The
regions inside | |
256 // window rectangless or outside the scissor should still have the initial c
heckerboard intact. | |
257 // (This verifies we didn't spend any time modifying those pixels in the mas
k.) | |
258 dc->drawPaint(StencilOnlyClip(), paint, SkMatrix::I()); | |
259 } | |
260 | |
261 void WindowRectanglesMaskGM::stencilCheckerboard(GrDrawContext* dc, bool flip) { | |
262 constexpr static GrUserStencilSettings kSetClip( | |
263 GrUserStencilSettings::StaticInit< | |
264 0, | |
265 GrUserStencilTest::kAlways, | |
266 0, | |
267 GrUserStencilOp::kSetClipBit, | |
268 GrUserStencilOp::kKeep, | |
269 0>() | |
270 ); | |
271 | |
272 dc->drawContextPriv().clearStencilClip(GrFixedClip::Disabled(), false); | |
273 | |
274 for (int y = 0; y < kLayerRect.height(); y += kMaskCheckerSize) { | |
275 for (int x = (y & 1) == flip ? 0 : kMaskCheckerSize; | |
276 x < kLayerRect.width(); x += 2 * kMaskCheckerSize) { | |
277 SkIRect checker = SkIRect::MakeXYWH(x, y, kMaskCheckerSize, kMaskChe
ckerSize); | |
278 dc->drawContextPriv().stencilRect(GrNoClip(), &kSetClip, false, SkMa
trix::I(), | |
279 SkRect::Make(checker)); | |
280 } | |
281 } | |
282 } | |
283 | |
284 void WindowRectanglesMaskGM::fail(SkCanvas* canvas) { | |
285 SkPaint paint; | |
286 paint.setAntiAlias(true); | |
287 paint.setTextAlign(SkPaint::kCenter_Align); | |
288 paint.setTextSize(20); | |
289 sk_tool_utils::set_portable_typeface(&paint); | |
290 | |
291 SkString errorMsg; | |
292 errorMsg.printf("Requires GPU with %i window rectangles", kNumWindows); | |
293 | |
294 canvas->clipRect(SkRect::Make(kCoverRect)); | |
295 canvas->clear(SK_ColorWHITE); | |
296 canvas->drawText(errorMsg.c_str(), errorMsg.size(), SkIntToScalar(kCoverRect
.centerX()), | |
297 SkIntToScalar(kCoverRect.centerY() - 10), paint); | |
298 } | |
299 | |
300 DEF_GM( return new WindowRectanglesMaskGM(); ) | |
301 | |
302 #endif | |
303 | |
304 } | |
OLD | NEW |