blob: 6766de2e3b2370d5fe835f03133e9e31bdadc7fc [file] [log] [blame]
joshualitt3566d442015-09-18 07:12:55 -07001/*
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 "GrNonAAStrokeRectBatch.h"
9
10#include "GrBatchTest.h"
11#include "GrBatchFlushState.h"
12#include "GrColor.h"
13#include "GrDefaultGeoProcFactory.h"
14#include "GrVertexBatch.h"
15#include "SkRandom.h"
16
17/* create a triangle strip that strokes the specified rect. There are 8
18 unique vertices, but we repeat the last 2 to close up. Alternatively we
19 could use an indices array, and then only send 8 verts, but not sure that
20 would be faster.
21 */
22static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
23 const SkScalar rad = SkScalarHalf(width);
24 // TODO we should be able to enable this assert, but we'd have to filter these draws
25 // this is a bug
26 //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
27
28 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
29 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
30 verts[2].set(rect.fRight - rad, rect.fTop + rad);
31 verts[3].set(rect.fRight + rad, rect.fTop - rad);
32 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
33 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
34 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
35 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
36 verts[8] = verts[0];
37 verts[9] = verts[1];
38}
39
40class NonAAStrokeRectBatch : public GrVertexBatch {
41public:
42 DEFINE_BATCH_CLASS_ID
43
44 struct Geometry {
45 SkMatrix fViewMatrix;
46 SkRect fRect;
47 SkScalar fStrokeWidth;
48 GrColor fColor;
49 };
50
joshualittaa37a962015-09-18 13:03:25 -070051 static NonAAStrokeRectBatch* Create() {
52 return new NonAAStrokeRectBatch;
joshualitt3566d442015-09-18 07:12:55 -070053 }
54
55 const char* name() const override { return "GrStrokeRectBatch"; }
56
ethannicholasff210322015-11-24 12:10:10 -080057 void computePipelineOptimizations(GrInitInvariantOutput* color,
58 GrInitInvariantOutput* coverage,
59 GrBatchToXPOverrides* overrides) const override {
joshualitt3566d442015-09-18 07:12:55 -070060 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -080061 color->setKnownFourComponents(fGeoData[0].fColor);
62 coverage->setKnownSingleComponent(0xff);
joshualitt3566d442015-09-18 07:12:55 -070063 }
64
joshualittaa37a962015-09-18 13:03:25 -070065 void append(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
66 SkScalar strokeWidth) {
67 Geometry& geometry = fGeoData.push_back();
68 geometry.fViewMatrix = viewMatrix;
69 geometry.fRect = rect;
70 geometry.fStrokeWidth = strokeWidth;
71 geometry.fColor = color;
joshualitt144c3c82015-11-30 12:30:13 -080072
73 // Sort the rect for hairlines
74 geometry.fRect.sort();
joshualittaa37a962015-09-18 13:03:25 -070075 }
76
77 void appendAndUpdateBounds(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
78 SkScalar strokeWidth, bool snapToPixelCenters) {
79 this->append(color, viewMatrix, rect, strokeWidth);
80
81 SkRect bounds;
82 this->setupBounds(&bounds, fGeoData.back(), snapToPixelCenters);
83 this->joinBounds(bounds);
84 }
85
86 void init(bool snapToPixelCenters) {
87 const Geometry& geo = fGeoData[0];
88 fBatch.fHairline = geo.fStrokeWidth == 0;
89
90 // setup bounds
91 this->setupBounds(&fBounds, geo, snapToPixelCenters);
92 }
93
joshualitt3566d442015-09-18 07:12:55 -070094private:
joshualittaa37a962015-09-18 13:03:25 -070095 void setupBounds(SkRect* bounds, const Geometry& geo, bool snapToPixelCenters) {
96 *bounds = geo.fRect;
97 SkScalar rad = SkScalarHalf(geo.fStrokeWidth);
98 bounds->outset(rad, rad);
99 geo.fViewMatrix.mapRect(&fBounds);
100
101 // If our caller snaps to pixel centers then we have to round out the bounds
102 if (snapToPixelCenters) {
103 bounds->roundOut();
104 }
105 }
106
joshualitt144c3c82015-11-30 12:30:13 -0800107 void onPrepareDraws(Target* target) const override {
joshualitt3566d442015-09-18 07:12:55 -0700108 SkAutoTUnref<const GrGeometryProcessor> gp;
109 {
110 using namespace GrDefaultGeoProcFactory;
111 Color color(this->color());
112 Coverage coverage(this->coverageIgnored() ? Coverage::kSolid_Type :
113 Coverage::kNone_Type);
114 LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
115 LocalCoords::kUnused_Type);
116 gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
117 this->viewMatrix()));
118 }
119
120 target->initDraw(gp, this->pipeline());
121
122 size_t vertexStride = gp->getVertexStride();
123
124 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
125
joshualitt144c3c82015-11-30 12:30:13 -0800126 const Geometry& args = fGeoData[0];
joshualitt3566d442015-09-18 07:12:55 -0700127
128 int vertexCount = kVertsPerHairlineRect;
129 if (args.fStrokeWidth > 0) {
130 vertexCount = kVertsPerStrokeRect;
131 }
132
133 const GrVertexBuffer* vertexBuffer;
134 int firstVertex;
135
136 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
137 &firstVertex);
138
139 if (!verts) {
140 SkDebugf("Could not allocate vertices\n");
141 return;
142 }
143
144 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
145
146 GrPrimitiveType primType;
joshualitt3566d442015-09-18 07:12:55 -0700147 if (args.fStrokeWidth > 0) {;
148 primType = kTriangleStrip_GrPrimitiveType;
joshualitt3566d442015-09-18 07:12:55 -0700149 init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
150 } else {
151 // hairline
152 primType = kLineStrip_GrPrimitiveType;
153 vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
154 vertex[1].set(args.fRect.fRight, args.fRect.fTop);
155 vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
156 vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
157 vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
158 }
159
160 GrVertices vertices;
161 vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
162 target->draw(vertices);
163 }
164
ethannicholasff210322015-11-24 12:10:10 -0800165 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt3566d442015-09-18 07:12:55 -0700166 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800167 if (!overrides.readsColor()) {
joshualitt3566d442015-09-18 07:12:55 -0700168 fGeoData[0].fColor = GrColor_ILLEGAL;
169 }
ethannicholasff210322015-11-24 12:10:10 -0800170 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt3566d442015-09-18 07:12:55 -0700171
172 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800173 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt3566d442015-09-18 07:12:55 -0700174 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800175 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
176 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt3566d442015-09-18 07:12:55 -0700177 }
178
joshualittaa37a962015-09-18 13:03:25 -0700179 NonAAStrokeRectBatch() : INHERITED(ClassID()) {}
joshualitt3566d442015-09-18 07:12:55 -0700180
181 GrColor color() const { return fBatch.fColor; }
182 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
183 bool colorIgnored() const { return fBatch.fColorIgnored; }
184 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
185 bool hairline() const { return fBatch.fHairline; }
186 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
187
188 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
189 // if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *t->pipeline(),
190 // t->bounds(), caps)) {
191 // return false;
192 // }
193 // GrStrokeRectBatch* that = t->cast<StrokeRectBatch>();
194
195 // NonAA stroke rects cannot batch right now
196 // TODO make these batchable
197 return false;
198 }
199
200 struct BatchTracker {
201 GrColor fColor;
202 bool fUsesLocalCoords;
203 bool fColorIgnored;
204 bool fCoverageIgnored;
205 bool fHairline;
206 };
207
208 const static int kVertsPerHairlineRect = 5;
209 const static int kVertsPerStrokeRect = 10;
210
211 BatchTracker fBatch;
212 SkSTArray<1, Geometry, true> fGeoData;
213
214 typedef GrVertexBatch INHERITED;
215};
216
217namespace GrNonAAStrokeRectBatch {
218
219GrDrawBatch* Create(GrColor color,
220 const SkMatrix& viewMatrix,
221 const SkRect& rect,
222 SkScalar strokeWidth,
223 bool snapToPixelCenters) {
joshualittaa37a962015-09-18 13:03:25 -0700224 NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create();
225 batch->append(color, viewMatrix, rect, strokeWidth);
226 batch->init(snapToPixelCenters);
227 return batch;
228}
229
230void Append(GrBatch* origBatch,
231 GrColor color,
232 const SkMatrix& viewMatrix,
233 const SkRect& rect,
234 SkScalar strokeWidth,
235 bool snapToPixelCenters) {
236 NonAAStrokeRectBatch* batch = origBatch->cast<NonAAStrokeRectBatch>();
237 batch->appendAndUpdateBounds(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
joshualitt3566d442015-09-18 07:12:55 -0700238}
239
240};
241
242#ifdef GR_TEST_UTILS
243
244DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
245 SkMatrix viewMatrix = GrTest::TestMatrix(random);
246 GrColor color = GrRandomColor(random);
247 SkRect rect = GrTest::TestRect(random);
248 SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f;
249
joshualittaa37a962015-09-18 13:03:25 -0700250 return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
joshualitt3566d442015-09-18 07:12:55 -0700251}
252
253#endif