| 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 |