1 /* 2 * Copyright 2014 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 // This test only works with the GPU backend. 9 10 #include "gm.h" 11 12 #if SK_SUPPORT_GPU 13 14 #include "GrContext.h" 15 #include "GrDefaultGeoProcFactory.h" 16 #include "GrOpFlushState.h" 17 #include "GrPathUtils.h" 18 #include "GrRenderTargetContextPriv.h" 19 #include "GrTest.h" 20 #include "SkColorPriv.h" 21 #include "SkGeometry.h" 22 #include "SkTLList.h" 23 #include "effects/GrConvexPolyEffect.h" 24 #include "ops/GrMeshDrawOp.h" 25 26 /** outset rendered rect to visualize anti-aliased poly edges */ 27 static SkRect outset(const SkRect& unsorted) { 28 SkRect r = unsorted; 29 r.outset(5.f, 5.f); 30 return r; 31 } 32 33 /** sorts a rect */ 34 static SkRect sorted_rect(const SkRect& unsorted) { 35 SkRect r = unsorted; 36 r.sort(); 37 return r; 38 } 39 40 namespace skiagm { 41 class PolyBoundsOp : public GrMeshDrawOp { 42 public: 43 DEFINE_OP_CLASS_ID 44 45 const char* name() const override { return "PolyBoundsOp"; } 46 47 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkRect& rect) { 48 return std::unique_ptr<GrDrawOp>(new PolyBoundsOp(std::move(paint), rect)); 49 } 50 51 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 52 53 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 54 auto analysis = fProcessors.finalize( 55 fColor, GrProcessorAnalysisCoverage::kNone, clip, false, caps, &fColor); 56 return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo; 57 } 58 59 private: 60 PolyBoundsOp(GrPaint&& paint, const SkRect& rect) 61 : INHERITED(ClassID()) 62 , fColor(paint.getColor()) 63 , fProcessors(std::move(paint)) 64 , fRect(outset(rect)) { 65 this->setBounds(sorted_rect(fRect), HasAABloat::kNo, IsZeroArea::kNo); 66 } 67 68 void onPrepareDraws(Target* target) const override { 69 using namespace GrDefaultGeoProcFactory; 70 71 Color color(fColor); 72 sk_sp<GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Make( 73 color, Coverage::kSolid_Type, LocalCoords::kUnused_Type, SkMatrix::I())); 74 75 size_t vertexStride = gp->getVertexStride(); 76 SkASSERT(vertexStride == sizeof(SkPoint)); 77 QuadHelper helper; 78 SkPoint* verts = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1)); 79 if (!verts) { 80 return; 81 } 82 83 fRect.toQuad(verts); 84 85 helper.recordDraw(target, gp.get(), target->makePipeline(0, &fProcessors)); 86 } 87 88 bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; } 89 90 GrColor fColor; 91 GrProcessorSet fProcessors; 92 SkRect fRect; 93 94 typedef GrMeshDrawOp INHERITED; 95 }; 96 97 /** 98 * This GM directly exercises a GrProcessor that draws convex polygons. 99 */ 100 class ConvexPolyEffect : public GM { 101 public: 102 ConvexPolyEffect() { 103 this->setBGColor(0xFFFFFFFF); 104 } 105 106 protected: 107 SkString onShortName() override { 108 return SkString("convex_poly_effect"); 109 } 110 111 SkISize onISize() override { 112 return SkISize::Make(720, 800); 113 } 114 115 void onOnceBeforeDraw() override { 116 SkPath tri; 117 tri.moveTo(5.f, 5.f); 118 tri.lineTo(100.f, 20.f); 119 tri.lineTo(15.f, 100.f); 120 121 fPaths.addToTail(tri); 122 fPaths.addToTail(SkPath())->reverseAddPath(tri); 123 124 tri.close(); 125 fPaths.addToTail(tri); 126 127 SkPath ngon; 128 constexpr SkScalar kRadius = 50.f; 129 const SkPoint center = { kRadius, kRadius }; 130 for (int i = 0; i < GrConvexPolyEffect::kMaxEdges; ++i) { 131 SkScalar angle = 2 * SK_ScalarPI * i / GrConvexPolyEffect::kMaxEdges; 132 SkPoint point; 133 point.fY = SkScalarSinCos(angle, &point.fX); 134 point.scale(kRadius); 135 point = center + point; 136 if (0 == i) { 137 ngon.moveTo(point); 138 } else { 139 ngon.lineTo(point); 140 } 141 } 142 143 fPaths.addToTail(ngon); 144 SkMatrix scaleM; 145 scaleM.setScale(1.1f, 0.4f); 146 ngon.transform(scaleM); 147 fPaths.addToTail(ngon); 148 149 SkPath linePath; 150 linePath.moveTo(5.f, 5.f); 151 linePath.lineTo(6.f, 6.f); 152 fPaths.addToTail(linePath); 153 154 // integer edges 155 fRects.addToTail(SkRect::MakeLTRB(5.f, 1.f, 30.f, 25.f)); 156 // half-integer edges 157 fRects.addToTail(SkRect::MakeLTRB(5.5f, 0.5f, 29.5f, 24.5f)); 158 // vertically/horizontally thin rects that cover pixel centers 159 fRects.addToTail(SkRect::MakeLTRB(5.25f, 0.5f, 5.75f, 24.5f)); 160 fRects.addToTail(SkRect::MakeLTRB(5.5f, 0.5f, 29.5f, 0.75f)); 161 // vertically/horizontally thin rects that don't cover pixel centers 162 fRects.addToTail(SkRect::MakeLTRB(5.55f, 0.5f, 5.75f, 24.5f)); 163 fRects.addToTail(SkRect::MakeLTRB(5.5f, .05f, 29.5f, .25f)); 164 // small in x and y 165 fRects.addToTail(SkRect::MakeLTRB(5.05f, .55f, 5.45f, .85f)); 166 // inverted in x and y 167 fRects.addToTail(SkRect::MakeLTRB(100.f, 50.5f, 5.f, 0.5f)); 168 } 169 170 void onDraw(SkCanvas* canvas) override { 171 GrRenderTargetContext* renderTargetContext = 172 canvas->internal_private_accessTopLayerRenderTargetContext(); 173 if (!renderTargetContext) { 174 skiagm::GM::DrawGpuOnlyMessage(canvas); 175 return; 176 } 177 178 SkScalar y = 0; 179 constexpr SkScalar kDX = 12.f; 180 for (PathList::Iter iter(fPaths, PathList::Iter::kHead_IterStart); 181 iter.get(); 182 iter.next()) { 183 const SkPath* path = iter.get(); 184 SkScalar x = 0; 185 186 for (int et = 0; et < kGrProcessorEdgeTypeCnt; ++et) { 187 const SkMatrix m = SkMatrix::MakeTrans(x, y); 188 SkPath p; 189 path->transform(m, &p); 190 191 GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et; 192 sk_sp<GrFragmentProcessor> fp(GrConvexPolyEffect::Make(edgeType, p)); 193 if (!fp) { 194 continue; 195 } 196 197 GrPaint grPaint; 198 grPaint.setColor4f(GrColor4f(0, 0, 0, 1.f)); 199 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 200 grPaint.addCoverageFragmentProcessor(std::move(fp)); 201 202 std::unique_ptr<GrDrawOp> op = 203 PolyBoundsOp::Make(std::move(grPaint), p.getBounds()); 204 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 205 206 x += SkScalarCeilToScalar(path->getBounds().width() + kDX); 207 } 208 209 // Draw AA and non AA paths using normal API for reference. 210 canvas->save(); 211 canvas->translate(x, y); 212 SkPaint paint; 213 canvas->drawPath(*path, paint); 214 canvas->translate(path->getBounds().width() + 10.f, 0); 215 paint.setAntiAlias(true); 216 canvas->drawPath(*path, paint); 217 canvas->restore(); 218 219 y += SkScalarCeilToScalar(path->getBounds().height() + 20.f); 220 } 221 222 for (RectList::Iter iter(fRects, RectList::Iter::kHead_IterStart); 223 iter.get(); 224 iter.next()) { 225 226 SkScalar x = 0; 227 228 for (int et = 0; et < kGrProcessorEdgeTypeCnt; ++et) { 229 SkRect rect = *iter.get(); 230 rect.offset(x, y); 231 GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et; 232 sk_sp<GrFragmentProcessor> fp(GrConvexPolyEffect::Make(edgeType, rect)); 233 if (!fp) { 234 continue; 235 } 236 237 GrPaint grPaint; 238 grPaint.setColor4f(GrColor4f(0, 0, 0, 1.f)); 239 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 240 grPaint.addCoverageFragmentProcessor(std::move(fp)); 241 242 std::unique_ptr<GrDrawOp> op = PolyBoundsOp::Make(std::move(grPaint), rect); 243 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 244 245 x += SkScalarCeilToScalar(rect.width() + kDX); 246 } 247 248 // Draw rect without and with AA using normal API for reference 249 canvas->save(); 250 canvas->translate(x, y); 251 SkPaint paint; 252 canvas->drawRect(*iter.get(), paint); 253 x += SkScalarCeilToScalar(iter.get()->width() + kDX); 254 paint.setAntiAlias(true); 255 canvas->drawRect(*iter.get(), paint); 256 canvas->restore(); 257 258 y += SkScalarCeilToScalar(iter.get()->height() + 20.f); 259 } 260 } 261 262 private: 263 typedef SkTLList<SkPath, 1> PathList; 264 typedef SkTLList<SkRect, 1> RectList; 265 PathList fPaths; 266 RectList fRects; 267 268 typedef GM INHERITED; 269 }; 270 271 DEF_GM(return new ConvexPolyEffect;) 272 } 273 274 #endif 275