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 class BasicClipVisitor : public SkCanvasClipVisitor { |
| 77 public: |
| 78 typedef SkRegion::Op Op; |
| 79 BasicClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {} |
| 80 void clipRect(const SkRect& r, Op op, bool aa) final { fCanvas->clipRect(r,
op, aa); } |
| 81 void clipRRect(const SkRRect& rr, Op op, bool aa) final { fCanvas->clipRRect
(rr, op, aa); } |
| 82 void clipPath(const SkPath&, Op, bool) final { SkFAIL("Not implemented"); } |
| 83 private: |
| 84 SkCanvas* const fCanvas; |
| 85 }; |
| 86 |
| 87 void WindowRectanglesGM::onCoverClipStack(const SkClipStack& stack, SkCanvas* ca
nvas) { |
| 88 SkPaint paint; |
| 89 paint.setColor(0xff00aa80); |
| 90 |
| 91 BasicClipVisitor visitor(canvas); |
| 92 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); |
| 93 for (const SkClipStack::Element* element = iter.next(); element; element = i
ter.next()) { |
| 94 element->replay(&visitor); |
| 95 } |
| 96 |
| 97 canvas->drawRect(SkRect::Make(kCoverRect), paint); |
| 98 } |
| 99 |
| 100 DEF_GM( return new WindowRectanglesGM(); ) |
| 101 |
| 102 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
| 103 |
| 104 #if SK_SUPPORT_GPU |
| 105 |
| 106 /** |
| 107 * Visualizes the mask (alpha or stencil) for a clip with several window rectang
les. The regions |
| 108 * inside window rectangles should be left untouched and appear as a small check
erboard pattern. |
| 109 */ |
| 110 class WindowRectanglesMaskGM : public WindowRectanglesBaseGM { |
| 111 private: |
| 112 constexpr static int kMaskCheckerSize = 5; |
| 113 SkString onShortName() final { return SkString("windowrectangles_mask"); } |
| 114 void onCoverClipStack(const SkClipStack&, SkCanvas*) final; |
| 115 void visualizeAlphaMask(GrContext*, GrDrawContext*, const GrReducedClip&, co
nst GrPaint&); |
| 116 void visualizeStencilMask(GrContext*, GrDrawContext*, const GrReducedClip&,
const GrPaint&); |
| 117 void stencilCheckerboard(GrDrawContext*, bool flip); |
| 118 void fail(SkCanvas*); |
| 119 }; |
| 120 |
| 121 class MaskOnlyClip : public GrClip { |
| 122 private: |
| 123 bool quickContains(const SkRect&) const final { return false; } |
| 124 bool isRRect(const SkRect& rtBounds, SkRRect* rr, bool* aa) const final { re
turn false; } |
| 125 void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior)
const final { |
| 126 rect->set(0, 0, width, height); |
| 127 if (iior) { |
| 128 *iior = false; |
| 129 } |
| 130 } |
| 131 }; |
| 132 |
| 133 class AlphaOnlyClip : public MaskOnlyClip { |
| 134 public: |
| 135 AlphaOnlyClip(GrTexture* mask, SkScalar x, SkScalar y) { |
| 136 int w = mask->width(), h = mask->height(); |
| 137 SkMatrix mat = SkMatrix::MakeScale(1.f / SkIntToScalar(w), 1.f / SkIntTo
Scalar(h)); |
| 138 mat.preTranslate(-x, -y); |
| 139 fFP = GrTextureDomainEffect::Make( |
| 140 mask, nullptr, mat, |
| 141 GrTextureDomain::MakeTexelDomain(mask, SkIRect::MakeWH(w, h)), |
| 142 GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMod
e, |
| 143 kDevice_GrCoordSet); |
| 144 |
| 145 } |
| 146 private: |
| 147 bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const
final { |
| 148 out->addCoverageFP(fFP); |
| 149 return true; |
| 150 } |
| 151 sk_sp<GrFragmentProcessor> fFP; |
| 152 }; |
| 153 |
| 154 class StencilOnlyClip : public MaskOnlyClip { |
| 155 private: |
| 156 bool apply(GrContext*, GrDrawContext*, bool, bool, GrAppliedClip* out) const
final { |
| 157 out->addStencilClip(); |
| 158 return true; |
| 159 } |
| 160 }; |
| 161 |
| 162 void WindowRectanglesMaskGM::onCoverClipStack(const SkClipStack& stack, SkCanvas
* canvas) { |
| 163 GrContext* ctx = canvas->getGrContext(); |
| 164 GrDrawContext* dc = canvas->internal_private_accessTopLayerDrawContext(); |
| 165 |
| 166 if (!ctx || !dc || |
| 167 dc->accessRenderTarget()->renderTargetPriv().maxWindowRectangles() < kNu
mWindows) { |
| 168 this->fail(canvas); |
| 169 return; |
| 170 } |
| 171 |
| 172 const GrReducedClip reducedClip(stack, SkRect::Make(kCoverRect), kNumWindows
); |
| 173 |
| 174 GrPaint paint; |
| 175 paint.setAntiAlias(true); |
| 176 if (!dc->isStencilBufferMultisampled()) { |
| 177 paint.setColor4f(GrColor4f(0, 0.25f, 1, 1)); |
| 178 this->visualizeAlphaMask(ctx, dc, reducedClip, paint); |
| 179 } else { |
| 180 paint.setColor4f(GrColor4f(1, 0.25f, 0.25f, 1)); |
| 181 this->visualizeStencilMask(ctx, dc, reducedClip, paint); |
| 182 } |
| 183 } |
| 184 |
| 185 void WindowRectanglesMaskGM::visualizeAlphaMask(GrContext* ctx, GrDrawContext* d
c, |
| 186 const GrReducedClip& reducedClip
, |
| 187 const GrPaint& paint) { |
| 188 GrPixelConfig config = kRGBA_8888_GrPixelConfig; |
| 189 if (ctx->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { |
| 190 config = kAlpha_8_GrPixelConfig; |
| 191 } |
| 192 |
| 193 sk_sp<GrDrawContext> maskDC(ctx->makeDrawContext(SkBackingFit::kExact, kLaye
rRect.width(), |
| 194 kLayerRect.height(), config
, nullptr)); |
| 195 if (!maskDC || |
| 196 !ctx->resourceProvider()->attachStencilAttachment(maskDC->accessRenderTa
rget())) { |
| 197 return; |
| 198 } |
| 199 |
| 200 // Draw a checker pattern into the alpha mask so we can visualize the region
s left untouched by |
| 201 // the clip mask generation. |
| 202 this->stencilCheckerboard(maskDC.get(), true); |
| 203 maskDC->clear(nullptr, GrColorPackA4(0xff), true); |
| 204 maskDC->drawContextPriv().drawAndStencilRect(StencilOnlyClip(), &GrUserStenc
ilSettings::kUnused, |
| 205 SkRegion::kDifference_Op, false
, false, |
| 206 SkMatrix::I(), |
| 207 SkRect::MakeIWH(maskDC->width()
, maskDC->height())); |
| 208 reducedClip.drawAlphaClipMask(maskDC.get()); |
| 209 sk_sp<GrTexture> mask(maskDC->asTexture()); |
| 210 |
| 211 SkScalar x = SkIntToScalar(kCoverRect.x() - kLayerRect.x()), |
| 212 y = SkIntToScalar(kCoverRect.y() - kLayerRect.y()); |
| 213 |
| 214 AlphaOnlyClip clip(mask.get(), x, y); |
| 215 dc->drawRect(clip, paint, SkMatrix::I(), SkRect::MakeXYWH(x, y, mask->width(
), mask->height())); |
| 216 } |
| 217 |
| 218 void WindowRectanglesMaskGM::visualizeStencilMask(GrContext* ctx, GrDrawContext*
dc, |
| 219 const GrReducedClip& reducedCl
ip, |
| 220 const GrPaint& paint) { |
| 221 if (!ctx->resourceProvider()->attachStencilAttachment(dc->accessRenderTarget
())) { |
| 222 return; |
| 223 } |
| 224 |
| 225 // Draw a checker pattern into the stencil buffer so we can visualize the re
gions left untouched |
| 226 // by the clip mask generation. |
| 227 this->stencilCheckerboard(dc, false); |
| 228 reducedClip.drawStencilClipMask(ctx, dc, {kLayerRect.x(), kLayerRect.y()}); |
| 229 |
| 230 dc->drawPaint(StencilOnlyClip(), paint, SkMatrix::I()); |
| 231 } |
| 232 |
| 233 void WindowRectanglesMaskGM::stencilCheckerboard(GrDrawContext* dc, bool flip) { |
| 234 constexpr static GrUserStencilSettings kSetClip( |
| 235 GrUserStencilSettings::StaticInit< |
| 236 0, |
| 237 GrUserStencilTest::kAlways, |
| 238 0, |
| 239 GrUserStencilOp::kSetClipBit, |
| 240 GrUserStencilOp::kKeep, |
| 241 0>() |
| 242 ); |
| 243 |
| 244 dc->drawContextPriv().clearStencilClip(GrFixedClip::Disabled(), false); |
| 245 |
| 246 for (int y = 0; y < kLayerRect.height(); y += kMaskCheckerSize) { |
| 247 for (int x = (y & 1) == flip ? 0 : kMaskCheckerSize; |
| 248 x < kLayerRect.width(); x += 2 * kMaskCheckerSize) { |
| 249 SkIRect checker = SkIRect::MakeXYWH(x, y, kMaskCheckerSize, kMaskChe
ckerSize); |
| 250 dc->drawContextPriv().stencilRect(GrNoClip(), &kSetClip, false, SkMa
trix::I(), |
| 251 SkRect::Make(checker)); |
| 252 } |
| 253 } |
| 254 } |
| 255 |
| 256 void WindowRectanglesMaskGM::fail(SkCanvas* canvas) { |
| 257 SkPaint paint; |
| 258 paint.setAntiAlias(true); |
| 259 paint.setTextAlign(SkPaint::kCenter_Align); |
| 260 paint.setTextSize(20); |
| 261 sk_tool_utils::set_portable_typeface(&paint); |
| 262 |
| 263 SkString errorMsg; |
| 264 errorMsg.printf("Requires GPU with %i window rectangles", kNumWindows); |
| 265 |
| 266 canvas->clipRect(SkRect::Make(kCoverRect)); |
| 267 canvas->clear(SK_ColorWHITE); |
| 268 canvas->drawText(errorMsg.c_str(), errorMsg.size(), SkIntToScalar(kCoverRect
.centerX()), |
| 269 SkIntToScalar(kCoverRect.centerY() - 10), paint); |
| 270 } |
| 271 |
| 272 DEF_GM( return new WindowRectanglesMaskGM(); ) |
| 273 |
| 274 #endif |
| 275 |
| 276 } |
OLD | NEW |