• 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     // 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.
allowed_stroke(const SkStrokeRec & stroke)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 
name() const58     const char* name() const override { return "NonAAStrokeRectOp"; }
59 
dumpInfo() const60     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 
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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 
NonAAStrokeRectOp(const Helper::MakeArgs & helperArgs,GrColor color,Helper::Flags flags,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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 
fixedFunctionFlags() const120     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
121 
finalize(const GrCaps & caps,const GrAppliedClip * clip)122     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
123         return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone,
124                                             &fColor);
125     }
126 
127 private:
onPrepareDraws(Target * target) const128     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 
onCombineIfPossible(GrOp * t,const GrCaps &)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 {
MakeNonAAStroke(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkStrokeRec & stroke,GrAAType aaType)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 
GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp)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