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 */
init_stroke_rect_strip(SkPoint verts[10],const SkRect & rect,SkScalar width)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.
allowed_stroke(const SkStrokeRec & stroke)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
name() const64 const char* name() const override { return "NonAAStrokeRectOp"; }
65
visitProxies(const VisitProxyFunc & func) const66 void visitProxies(const VisitProxyFunc& func) const override {
67 fHelper.visitProxies(func);
68 }
69
dumpInfo() const70 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
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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
NonAAStrokeRectOp(const Helper::MakeArgs & helperArgs,GrColor color,Helper::Flags flags,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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
fixedFunctionFlags() const130 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
131
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)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:
onPrepareDraws(Target * target)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
onCombineIfPossible(GrOp * t,const GrCaps &)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 {
MakeNonAAStroke(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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
GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp)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