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