1 /* 2 * Copyright 2012 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/gm.h" 9 #include "include/core/SkBlendMode.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkMatrix.h" 12 #include "include/core/SkPaint.h" 13 #include "include/core/SkPoint.h" 14 #include "include/core/SkRRect.h" 15 #include "include/core/SkRect.h" 16 #include "include/core/SkScalar.h" 17 #include "include/core/SkSize.h" 18 #include "include/core/SkString.h" 19 #include "include/core/SkTypes.h" 20 #include "include/gpu/GrContext.h" 21 #include "include/private/GrSharedEnums.h" 22 #include "include/private/GrTypesPriv.h" 23 #include "src/gpu/GrCaps.h" 24 #include "src/gpu/GrFragmentProcessor.h" 25 #include "src/gpu/GrPaint.h" 26 #include "src/gpu/GrRenderTargetContext.h" 27 #include "src/gpu/GrRenderTargetContextPriv.h" 28 #include "src/gpu/effects/GrPorterDuffXferProcessor.h" 29 #include "src/gpu/effects/GrRRectEffect.h" 30 #include "src/gpu/ops/GrDrawOp.h" 31 #include "src/gpu/ops/GrFillRectOp.h" 32 33 #include <memory> 34 #include <utility> 35 36 namespace skiagm { 37 38 /////////////////////////////////////////////////////////////////////////////// 39 40 class RRectGM : public GM { 41 public: 42 enum Type { 43 kBW_Draw_Type, 44 kAA_Draw_Type, 45 kBW_Clip_Type, 46 kAA_Clip_Type, 47 kEffect_Type, 48 }; RRectGM(Type type)49 RRectGM(Type type) : fType(type) { } 50 51 protected: 52 onOnceBeforeDraw()53 void onOnceBeforeDraw() override { 54 this->setBGColor(0xFFDDDDDD); 55 this->setUpRRects(); 56 } 57 onShortName()58 SkString onShortName() override { 59 SkString name("rrect"); 60 switch (fType) { 61 case kBW_Draw_Type: 62 name.append("_draw_bw"); 63 break; 64 case kAA_Draw_Type: 65 name.append("_draw_aa"); 66 break; 67 case kBW_Clip_Type: 68 name.append("_clip_bw"); 69 break; 70 case kAA_Clip_Type: 71 name.append("_clip_aa"); 72 break; 73 case kEffect_Type: 74 name.append("_effect"); 75 break; 76 } 77 return name; 78 } 79 onISize()80 SkISize onISize() override { return SkISize::Make(kImageWidth, kImageHeight); } 81 onDraw(SkCanvas * canvas,SkString * errorMsg)82 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { 83 GrRenderTargetContext* renderTargetContext = 84 canvas->internal_private_accessTopLayerRenderTargetContext(); 85 GrContext* context = canvas->getGrContext(); 86 if (kEffect_Type == fType && (!renderTargetContext || !context)) { 87 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 88 return DrawResult::kSkip; 89 } 90 91 SkPaint paint; 92 if (kAA_Draw_Type == fType) { 93 paint.setAntiAlias(true); 94 } 95 96 const SkRect kMaxTileBound = SkRect::MakeWH(SkIntToScalar(kTileX), 97 SkIntToScalar(kTileY)); 98 #ifdef SK_DEBUG 99 const SkRect kMaxImageBound = SkRect::MakeWH(SkIntToScalar(kImageWidth), 100 SkIntToScalar(kImageHeight)); 101 #endif 102 103 int lastEdgeType = (kEffect_Type == fType) ? (int) GrClipEdgeType::kLast: 0; 104 105 int y = 1; 106 for (int et = 0; et <= lastEdgeType; ++et) { 107 int x = 1; 108 for (int curRRect = 0; curRRect < kNumRRects; ++curRRect) { 109 bool drew = true; 110 #ifdef SK_DEBUG 111 SkASSERT(kMaxTileBound.contains(fRRects[curRRect].getBounds())); 112 SkRect imageSpaceBounds = fRRects[curRRect].getBounds(); 113 imageSpaceBounds.offset(SkIntToScalar(x), SkIntToScalar(y)); 114 SkASSERT(kMaxImageBound.contains(imageSpaceBounds)); 115 #endif 116 canvas->save(); 117 canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); 118 if (kEffect_Type == fType) { 119 SkRRect rrect = fRRects[curRRect]; 120 rrect.offset(SkIntToScalar(x), SkIntToScalar(y)); 121 GrClipEdgeType edgeType = (GrClipEdgeType) et; 122 const auto& caps = *renderTargetContext->caps()->shaderCaps(); 123 auto fp = GrRRectEffect::Make(edgeType, rrect, caps); 124 if (fp) { 125 GrPaint grPaint; 126 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 127 grPaint.addCoverageFragmentProcessor(std::move(fp)); 128 grPaint.setColor4f({ 0, 0, 0, 1.f }); 129 130 SkRect bounds = rrect.getBounds(); 131 bounds.outset(2.f, 2.f); 132 133 renderTargetContext->priv().testingOnly_addDrawOp( 134 GrFillRectOp::MakeNonAARect(context, std::move(grPaint), 135 SkMatrix::I(), bounds)); 136 } else { 137 drew = false; 138 } 139 } else if (kBW_Clip_Type == fType || kAA_Clip_Type == fType) { 140 bool aaClip = (kAA_Clip_Type == fType); 141 canvas->clipRRect(fRRects[curRRect], aaClip); 142 canvas->drawRect(kMaxTileBound, paint); 143 } else { 144 canvas->drawRRect(fRRects[curRRect], paint); 145 } 146 canvas->restore(); 147 if (drew) { 148 x = x + kTileX; 149 if (x > kImageWidth) { 150 x = 1; 151 y += kTileY; 152 } 153 } 154 } 155 if (x != 1) { 156 y += kTileY; 157 } 158 } 159 return DrawResult::kOk; 160 } 161 setUpRRects()162 void setUpRRects() { 163 // each RRect must fit in a 0x0 -> (kTileX-2)x(kTileY-2) block. These will be tiled across 164 // the screen in kTileX x kTileY tiles. The extra empty pixels on each side are for AA. 165 166 // simple cases 167 fRRects[0].setRect(SkRect::MakeWH(kTileX-2, kTileY-2)); 168 fRRects[1].setOval(SkRect::MakeWH(kTileX-2, kTileY-2)); 169 fRRects[2].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 10); 170 fRRects[3].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 5); 171 // small circular corners are an interesting test case for gpu clipping 172 fRRects[4].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 1, 1); 173 fRRects[5].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.5f, 0.5f); 174 fRRects[6].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.2f, 0.2f); 175 176 // The first complex case needs special handling since it is a square 177 fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]); 178 for (size_t i = 1; i < SK_ARRAY_COUNT(gRadii); ++i) { 179 fRRects[kNumSimpleCases+i].setRectRadii(SkRect::MakeWH(kTileX-2, kTileY-2), gRadii[i]); 180 } 181 } 182 183 private: 184 Type fType; 185 186 static constexpr int kImageWidth = 640; 187 static constexpr int kImageHeight = 480; 188 189 static constexpr int kTileX = 80; 190 static constexpr int kTileY = 40; 191 192 static constexpr int kNumSimpleCases = 7; 193 static constexpr int kNumComplexCases = 35; 194 static const SkVector gRadii[kNumComplexCases][4]; 195 196 static constexpr int kNumRRects = kNumSimpleCases + kNumComplexCases; 197 SkRRect fRRects[kNumRRects]; 198 199 typedef GM INHERITED; 200 }; 201 202 // Radii for the various test cases. Order is UL, UR, LR, LL 203 const SkVector RRectGM::gRadii[kNumComplexCases][4] = { 204 // a circle 205 { { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY } }, 206 207 // odd ball cases 208 { { 8, 8 }, { 32, 32 }, { 8, 8 }, { 32, 32 } }, 209 { { 16, 8 }, { 8, 16 }, { 16, 8 }, { 8, 16 } }, 210 { { 0, 0 }, { 16, 16 }, { 8, 8 }, { 32, 32 } }, 211 212 // UL 213 { { 30, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 214 { { 30, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 215 { { 15, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 216 217 // UR 218 { { 0, 0 }, { 30, 30 }, { 0, 0 }, { 0, 0 } }, 219 { { 0, 0 }, { 30, 15 }, { 0, 0 }, { 0, 0 } }, 220 { { 0, 0 }, { 15, 30 }, { 0, 0 }, { 0, 0 } }, 221 222 // LR 223 { { 0, 0 }, { 0, 0 }, { 30, 30 }, { 0, 0 } }, 224 { { 0, 0 }, { 0, 0 }, { 30, 15 }, { 0, 0 } }, 225 { { 0, 0 }, { 0, 0 }, { 15, 30 }, { 0, 0 } }, 226 227 // LL 228 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 30 } }, 229 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 15 } }, 230 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 30 } }, 231 232 // over-sized radii 233 { { 0, 0 }, { 100, 400 }, { 0, 0 }, { 0, 0 } }, 234 { { 0, 0 }, { 400, 400 }, { 0, 0 }, { 0, 0 } }, 235 { { 400, 400 }, { 400, 400 }, { 400, 400 }, { 400, 400 } }, 236 237 // circular corner tabs 238 { { 0, 0 }, { 20, 20 }, { 20, 20 }, { 0, 0 } }, 239 { { 20, 20 }, { 20, 20 }, { 0, 0 }, { 0, 0 } }, 240 { { 0, 0 }, { 0, 0 }, { 20, 20 }, { 20, 20 } }, 241 { { 20, 20 }, { 0, 0 }, { 0, 0 }, { 20, 20 } }, 242 243 // small radius circular corner tabs 244 { { 0, 0 }, { 0.2f, 0.2f }, { 0.2f, 0.2f }, { 0, 0 } }, 245 { { 0.3f, 0.3f }, { 0.3f, .3f }, { 0, 0 }, { 0, 0 } }, 246 247 // single circular corner cases 248 { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 15 } }, 249 { { 0, 0 }, { 0, 0 }, { 15, 15 }, { 0, 0 } }, 250 { { 0, 0 }, { 15, 15 }, { 0, 0 }, { 0, 0 } }, 251 { { 15, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 252 253 // nine patch elliptical 254 { { 5, 7 }, { 8, 7 }, { 8, 12 }, { 5, 12 } }, 255 { { 0, 7 }, { 8, 7 }, { 8, 12 }, { 0, 12 } }, 256 257 // nine patch elliptical, small radii 258 { { 0.4f, 7 }, { 8, 7 }, { 8, 12 }, { 0.4f, 12 } }, 259 { { 0.4f, 0.4f }, { 8, 0.4f }, { 8, 12 }, { 0.4f, 12 } }, 260 { { 20, 0.4f }, { 18, 0.4f }, { 18, 0.4f }, { 20, 0.4f } }, 261 { { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f } }, 262 263 }; 264 265 /////////////////////////////////////////////////////////////////////////////// 266 267 DEF_GM( return new RRectGM(RRectGM::kAA_Draw_Type); ) 268 DEF_GM( return new RRectGM(RRectGM::kBW_Draw_Type); ) 269 DEF_GM( return new RRectGM(RRectGM::kAA_Clip_Type); ) 270 DEF_GM( return new RRectGM(RRectGM::kBW_Clip_Type); ) 271 DEF_GM( return new RRectGM(RRectGM::kEffect_Type); ) 272 273 } 274