Line data Source code
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 "GrNonAAStrokeRectOp.h"
9 :
10 : #include "GrColor.h"
11 : #include "GrDefaultGeoProcFactory.h"
12 : #include "GrDrawOpTest.h"
13 : #include "GrMeshDrawOp.h"
14 : #include "GrOpFlushState.h"
15 : #include "SkStrokeRec.h"
16 : #include "SkRandom.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 0 : static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
24 0 : 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 0 : verts[0].set(rect.fLeft + rad, rect.fTop + rad);
30 0 : verts[1].set(rect.fLeft - rad, rect.fTop - rad);
31 0 : verts[2].set(rect.fRight - rad, rect.fTop + rad);
32 0 : verts[3].set(rect.fRight + rad, rect.fTop - rad);
33 0 : verts[4].set(rect.fRight - rad, rect.fBottom - rad);
34 0 : verts[5].set(rect.fRight + rad, rect.fBottom + rad);
35 0 : verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
36 0 : verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
37 0 : verts[8] = verts[0];
38 0 : verts[9] = verts[1];
39 0 : }
40 :
41 : // Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
42 0 : inline static bool allowed_stroke(const SkStrokeRec& stroke) {
43 0 : SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
44 : stroke.getStyle() == SkStrokeRec::kHairline_Style);
45 0 : return !stroke.getWidth() ||
46 0 : (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
47 : }
48 :
49 0 : class NonAAStrokeRectOp final : public GrLegacyMeshDrawOp {
50 : public:
51 0 : DEFINE_OP_CLASS_ID
52 :
53 0 : const char* name() const override { return "NonAAStrokeRectOp"; }
54 :
55 0 : SkString dumpInfo() const override {
56 0 : SkString string;
57 0 : string.appendf(
58 : "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
59 : "StrokeWidth: %.2f\n",
60 0 : fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
61 0 : string.append(DumpPipelineInfo(*this->pipeline()));
62 0 : string.append(INHERITED::dumpInfo());
63 0 : return string;
64 : }
65 :
66 0 : static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
67 : const SkRect& rect, const SkStrokeRec& stroke,
68 : bool snapToPixelCenters) {
69 0 : if (!allowed_stroke(stroke)) {
70 0 : return nullptr;
71 : }
72 0 : NonAAStrokeRectOp* op = new NonAAStrokeRectOp();
73 0 : op->fColor = color;
74 0 : op->fViewMatrix = viewMatrix;
75 0 : op->fRect = rect;
76 : // Sort the rect for hairlines
77 0 : op->fRect.sort();
78 0 : op->fStrokeWidth = stroke.getWidth();
79 :
80 0 : SkScalar rad = SkScalarHalf(op->fStrokeWidth);
81 0 : SkRect bounds = rect;
82 0 : bounds.outset(rad, rad);
83 :
84 : // If our caller snaps to pixel centers then we have to round out the bounds
85 0 : if (snapToPixelCenters) {
86 0 : viewMatrix.mapRect(&bounds);
87 : // We want to be consistent with how we snap non-aa lines. To match what we do in
88 : // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
89 : // pixel to force us to pixel centers.
90 0 : bounds.set(SkScalarFloorToScalar(bounds.fLeft),
91 : SkScalarFloorToScalar(bounds.fTop),
92 : SkScalarFloorToScalar(bounds.fRight),
93 0 : SkScalarFloorToScalar(bounds.fBottom));
94 0 : bounds.offset(0.5f, 0.5f);
95 0 : op->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
96 : } else {
97 0 : op->setTransformedBounds(bounds, op->fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
98 : }
99 0 : return std::unique_ptr<GrLegacyMeshDrawOp>(op);
100 : }
101 :
102 : private:
103 0 : NonAAStrokeRectOp() : INHERITED(ClassID()) {}
104 :
105 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
106 : GrProcessorAnalysisCoverage* coverage) const override {
107 0 : color->setToConstant(fColor);
108 0 : *coverage = GrProcessorAnalysisCoverage::kNone;
109 0 : }
110 :
111 0 : void onPrepareDraws(Target* target) const override {
112 0 : sk_sp<GrGeometryProcessor> gp;
113 : {
114 : using namespace GrDefaultGeoProcFactory;
115 0 : Color color(fColor);
116 0 : LocalCoords::Type localCoordsType = fNeedsLocalCoords
117 0 : ? LocalCoords::kUsePosition_Type
118 0 : : LocalCoords::kUnused_Type;
119 0 : gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
120 0 : fViewMatrix);
121 : }
122 :
123 0 : size_t vertexStride = gp->getVertexStride();
124 :
125 0 : SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
126 :
127 0 : int vertexCount = kVertsPerHairlineRect;
128 0 : if (fStrokeWidth > 0) {
129 0 : vertexCount = kVertsPerStrokeRect;
130 : }
131 :
132 : const GrBuffer* vertexBuffer;
133 : int firstVertex;
134 :
135 : void* verts =
136 0 : target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
137 :
138 0 : if (!verts) {
139 0 : SkDebugf("Could not allocate vertices\n");
140 0 : return;
141 : }
142 :
143 0 : SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
144 :
145 : GrPrimitiveType primType;
146 0 : if (fStrokeWidth > 0) {
147 0 : primType = kTriangleStrip_GrPrimitiveType;
148 0 : init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
149 : } else {
150 : // hairline
151 0 : primType = kLineStrip_GrPrimitiveType;
152 0 : vertex[0].set(fRect.fLeft, fRect.fTop);
153 0 : vertex[1].set(fRect.fRight, fRect.fTop);
154 0 : vertex[2].set(fRect.fRight, fRect.fBottom);
155 0 : vertex[3].set(fRect.fLeft, fRect.fBottom);
156 0 : vertex[4].set(fRect.fLeft, fRect.fTop);
157 : }
158 :
159 0 : GrMesh mesh;
160 0 : mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
161 0 : target->draw(gp.get(), this->pipeline(), mesh);
162 : }
163 :
164 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
165 0 : optimizations.getOverrideColorIfSet(&fColor);
166 0 : fNeedsLocalCoords = optimizations.readsLocalCoords();
167 0 : }
168 :
169 0 : bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
170 : // NonAA stroke rects cannot combine right now
171 : // TODO make these combinable.
172 0 : return false;
173 : }
174 :
175 : GrColor fColor;
176 : SkMatrix fViewMatrix;
177 : SkRect fRect;
178 : SkScalar fStrokeWidth;
179 : bool fNeedsLocalCoords;
180 :
181 : const static int kVertsPerHairlineRect = 5;
182 : const static int kVertsPerStrokeRect = 10;
183 :
184 : typedef GrLegacyMeshDrawOp INHERITED;
185 : };
186 :
187 : namespace GrNonAAStrokeRectOp {
188 :
189 0 : std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
190 : const SkMatrix& viewMatrix,
191 : const SkRect& rect,
192 : const SkStrokeRec& stroke,
193 : bool snapToPixelCenters) {
194 0 : return NonAAStrokeRectOp::Make(color, viewMatrix, rect, stroke, snapToPixelCenters);
195 : }
196 : }
197 :
198 : #if GR_TEST_UTILS
199 :
200 0 : DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
201 0 : SkMatrix viewMatrix = GrTest::TestMatrix(random);
202 0 : GrColor color = GrRandomColor(random);
203 0 : SkRect rect = GrTest::TestRect(random);
204 0 : SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
205 0 : SkPaint paint;
206 0 : paint.setStrokeWidth(strokeWidth);
207 0 : paint.setStyle(SkPaint::kStroke_Style);
208 0 : paint.setStrokeJoin(SkPaint::kMiter_Join);
209 0 : SkStrokeRec strokeRec(paint);
210 0 : return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, random->nextBool());
211 : }
212 :
213 : #endif
|