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