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