1 /* 2 * Copyright 2015 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 "GrColor.h" 9 #include "GrDefaultGeoProcFactory.h" 10 #include "GrDrawOpTest.h" 11 #include "GrMeshDrawOp.h" 12 #include "GrOpFlushState.h" 13 #include "GrRectOpFactory.h" 14 #include "GrSimpleMeshDrawOpHelper.h" 15 #include "SkRandom.h" 16 #include "SkStrokeRec.h" 17 18 /* create a triangle strip that strokes the specified rect. There are 8 19 unique vertices, but we repeat the last 2 to close up. Alternatively we 20 could use an indices array, and then only send 8 verts, but not sure that 21 would be faster. 22 */ 23 static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) { 24 const SkScalar rad = SkScalarHalf(width); 25 26 verts[0].set(rect.fLeft + rad, rect.fTop + rad); 27 verts[1].set(rect.fLeft - rad, rect.fTop - rad); 28 verts[2].set(rect.fRight - rad, rect.fTop + rad); 29 verts[3].set(rect.fRight + rad, rect.fTop - rad); 30 verts[4].set(rect.fRight - rad, rect.fBottom - rad); 31 verts[5].set(rect.fRight + rad, rect.fBottom + rad); 32 verts[6].set(rect.fLeft + rad, rect.fBottom - rad); 33 verts[7].set(rect.fLeft - rad, rect.fBottom + rad); 34 verts[8] = verts[0]; 35 verts[9] = verts[1]; 36 37 // TODO: we should be catching this higher up the call stack and just draw a single 38 // non-AA rect 39 if (2*rad >= rect.width()) { 40 verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX(); 41 } 42 if (2*rad >= rect.height()) { 43 verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY(); 44 } 45 } 46 47 // Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners. 48 inline static bool allowed_stroke(const SkStrokeRec& stroke) { 49 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style || 50 stroke.getStyle() == SkStrokeRec::kHairline_Style); 51 return !stroke.getWidth() || 52 (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2); 53 } 54 55 namespace { 56 57 class NonAAStrokeRectOp final : public GrMeshDrawOp { 58 private: 59 using Helper = GrSimpleMeshDrawOpHelper; 60 61 public: 62 DEFINE_OP_CLASS_ID 63 64 const char* name() const override { return "NonAAStrokeRectOp"; } 65 66 void visitProxies(const VisitProxyFunc& func) const override { 67 fHelper.visitProxies(func); 68 } 69 70 SkString dumpInfo() const override { 71 SkString string; 72 string.appendf( 73 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 74 "StrokeWidth: %.2f\n", 75 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth); 76 string += fHelper.dumpInfo(); 77 string += INHERITED::dumpInfo(); 78 return string; 79 } 80 81 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 82 const SkRect& rect, const SkStrokeRec& stroke, 83 GrAAType aaType) { 84 if (!allowed_stroke(stroke)) { 85 return nullptr; 86 } 87 Helper::Flags flags = Helper::Flags::kNone; 88 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of 89 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not 90 // when MSAA is enabled because it can cause ugly artifacts. 91 if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) { 92 flags |= Helper::Flags::kSnapVerticesToPixelCenters; 93 } 94 return Helper::FactoryHelper<NonAAStrokeRectOp>(std::move(paint), flags, viewMatrix, rect, 95 stroke, aaType); 96 } 97 98 NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, Helper::Flags flags, 99 const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke, 100 GrAAType aaType) 101 : INHERITED(ClassID()), fHelper(helperArgs, aaType, flags) { 102 fColor = color; 103 fViewMatrix = viewMatrix; 104 fRect = rect; 105 // Sort the rect for hairlines 106 fRect.sort(); 107 fStrokeWidth = stroke.getWidth(); 108 109 SkScalar rad = SkScalarHalf(fStrokeWidth); 110 SkRect bounds = rect; 111 bounds.outset(rad, rad); 112 113 // If our caller snaps to pixel centers then we have to round out the bounds 114 if (flags & Helper::Flags::kSnapVerticesToPixelCenters) { 115 viewMatrix.mapRect(&bounds); 116 // We want to be consistent with how we snap non-aa lines. To match what we do in 117 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a 118 // pixel to force us to pixel centers. 119 bounds.set(SkScalarFloorToScalar(bounds.fLeft), 120 SkScalarFloorToScalar(bounds.fTop), 121 SkScalarFloorToScalar(bounds.fRight), 122 SkScalarFloorToScalar(bounds.fBottom)); 123 bounds.offset(0.5f, 0.5f); 124 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo); 125 } else { 126 this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo); 127 } 128 } 129 130 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 131 132 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 133 GrPixelConfigIsClamped dstIsClamped) override { 134 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 135 GrProcessorAnalysisCoverage::kNone, &fColor); 136 } 137 138 private: 139 void onPrepareDraws(Target* target) override { 140 sk_sp<GrGeometryProcessor> gp; 141 { 142 using namespace GrDefaultGeoProcFactory; 143 Color color(fColor); 144 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords() 145 ? LocalCoords::kUsePosition_Type 146 : LocalCoords::kUnused_Type; 147 gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType, 148 fViewMatrix); 149 } 150 151 size_t vertexStride = gp->getVertexStride(); 152 153 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr)); 154 155 int vertexCount = kVertsPerHairlineRect; 156 if (fStrokeWidth > 0) { 157 vertexCount = kVertsPerStrokeRect; 158 } 159 160 const GrBuffer* vertexBuffer; 161 int firstVertex; 162 163 void* verts = 164 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex); 165 166 if (!verts) { 167 SkDebugf("Could not allocate vertices\n"); 168 return; 169 } 170 171 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts); 172 173 GrPrimitiveType primType; 174 if (fStrokeWidth > 0) { 175 primType = GrPrimitiveType::kTriangleStrip; 176 init_stroke_rect_strip(vertex, fRect, fStrokeWidth); 177 } else { 178 // hairline 179 primType = GrPrimitiveType::kLineStrip; 180 vertex[0].set(fRect.fLeft, fRect.fTop); 181 vertex[1].set(fRect.fRight, fRect.fTop); 182 vertex[2].set(fRect.fRight, fRect.fBottom); 183 vertex[3].set(fRect.fLeft, fRect.fBottom); 184 vertex[4].set(fRect.fLeft, fRect.fTop); 185 } 186 187 GrMesh mesh(primType); 188 mesh.setNonIndexedNonInstanced(vertexCount); 189 mesh.setVertexData(vertexBuffer, firstVertex); 190 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 191 } 192 193 bool onCombineIfPossible(GrOp* t, const GrCaps&) override { 194 // NonAA stroke rects cannot combine right now 195 // TODO make these combinable. 196 return false; 197 } 198 199 Helper fHelper; 200 GrColor fColor; 201 SkMatrix fViewMatrix; 202 SkRect fRect; 203 SkScalar fStrokeWidth; 204 205 const static int kVertsPerHairlineRect = 5; 206 const static int kVertsPerStrokeRect = 10; 207 208 typedef GrMeshDrawOp INHERITED; 209 }; 210 211 } // anonymous namespace 212 213 namespace GrRectOpFactory { 214 std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&& paint, 215 const SkMatrix& viewMatrix, 216 const SkRect& rect, 217 const SkStrokeRec& stroke, 218 GrAAType aaType) { 219 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke, aaType); 220 } 221 } // namespace GrRectOpFactory 222 223 #if GR_TEST_UTILS 224 225 GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) { 226 SkMatrix viewMatrix = GrTest::TestMatrix(random); 227 SkRect rect = GrTest::TestRect(random); 228 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f; 229 SkPaint strokePaint; 230 strokePaint.setStrokeWidth(strokeWidth); 231 strokePaint.setStyle(SkPaint::kStroke_Style); 232 strokePaint.setStrokeJoin(SkPaint::kMiter_Join); 233 SkStrokeRec strokeRec(strokePaint); 234 GrAAType aaType = GrAAType::kNone; 235 if (fsaaType == GrFSAAType::kUnifiedMSAA) { 236 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone; 237 } 238 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, strokeRec, aaType); 239 } 240 241 #endif 242