• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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