blob: 36092ea3f74bfca2d358d11546374aad6fe2655b [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
halcanary9d524f22016-03-29 09:03:52 -070057 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -080058 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
joshualitt3566d442015-09-18 07:12:55 -0700120 size_t vertexStride = gp->getVertexStride();
121
122 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
123
joshualitt144c3c82015-11-30 12:30:13 -0800124 const Geometry& args = fGeoData[0];
joshualitt3566d442015-09-18 07:12:55 -0700125
126 int vertexCount = kVertsPerHairlineRect;
127 if (args.fStrokeWidth > 0) {
128 vertexCount = kVertsPerStrokeRect;
129 }
130
cdalton397536c2016-03-25 12:15:03 -0700131 const GrBuffer* vertexBuffer;
joshualitt3566d442015-09-18 07:12:55 -0700132 int firstVertex;
133
134 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
135 &firstVertex);
136
137 if (!verts) {
138 SkDebugf("Could not allocate vertices\n");
139 return;
140 }
141
142 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
143
144 GrPrimitiveType primType;
egdaniel0e1853c2016-03-17 11:35:45 -0700145 if (args.fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700146 primType = kTriangleStrip_GrPrimitiveType;
joshualitt3566d442015-09-18 07:12:55 -0700147 init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
148 } else {
149 // hairline
150 primType = kLineStrip_GrPrimitiveType;
151 vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
152 vertex[1].set(args.fRect.fRight, args.fRect.fTop);
153 vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
154 vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
155 vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
156 }
157
egdaniel0e1853c2016-03-17 11:35:45 -0700158 GrMesh mesh;
159 mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
bsalomon342bfc22016-04-01 06:06:20 -0700160 target->draw(gp, mesh);
joshualitt3566d442015-09-18 07:12:55 -0700161 }
162
ethannicholasff210322015-11-24 12:10:10 -0800163 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
joshualitt3566d442015-09-18 07:12:55 -0700164 // Handle any color overrides
ethannicholasff210322015-11-24 12:10:10 -0800165 if (!overrides.readsColor()) {
joshualitt3566d442015-09-18 07:12:55 -0700166 fGeoData[0].fColor = GrColor_ILLEGAL;
167 }
ethannicholasff210322015-11-24 12:10:10 -0800168 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt3566d442015-09-18 07:12:55 -0700169
170 // setup batch properties
ethannicholasff210322015-11-24 12:10:10 -0800171 fBatch.fColorIgnored = !overrides.readsColor();
joshualitt3566d442015-09-18 07:12:55 -0700172 fBatch.fColor = fGeoData[0].fColor;
ethannicholasff210322015-11-24 12:10:10 -0800173 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
174 fBatch.fCoverageIgnored = !overrides.readsCoverage();
joshualitt3566d442015-09-18 07:12:55 -0700175 }
176
joshualittaa37a962015-09-18 13:03:25 -0700177 NonAAStrokeRectBatch() : INHERITED(ClassID()) {}
joshualitt3566d442015-09-18 07:12:55 -0700178
179 GrColor color() const { return fBatch.fColor; }
180 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
181 bool colorIgnored() const { return fBatch.fColorIgnored; }
182 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
183 bool hairline() const { return fBatch.fHairline; }
184 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
185
186 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
187 // if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *t->pipeline(),
188 // t->bounds(), caps)) {
189 // return false;
190 // }
191 // GrStrokeRectBatch* that = t->cast<StrokeRectBatch>();
192
193 // NonAA stroke rects cannot batch right now
194 // TODO make these batchable
195 return false;
196 }
197
198 struct BatchTracker {
199 GrColor fColor;
200 bool fUsesLocalCoords;
201 bool fColorIgnored;
202 bool fCoverageIgnored;
203 bool fHairline;
204 };
205
206 const static int kVertsPerHairlineRect = 5;
207 const static int kVertsPerStrokeRect = 10;
208
209 BatchTracker fBatch;
210 SkSTArray<1, Geometry, true> fGeoData;
211
212 typedef GrVertexBatch INHERITED;
213};
214
215namespace GrNonAAStrokeRectBatch {
216
217GrDrawBatch* Create(GrColor color,
218 const SkMatrix& viewMatrix,
219 const SkRect& rect,
220 SkScalar strokeWidth,
221 bool snapToPixelCenters) {
joshualittaa37a962015-09-18 13:03:25 -0700222 NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create();
223 batch->append(color, viewMatrix, rect, strokeWidth);
224 batch->init(snapToPixelCenters);
225 return batch;
226}
227
228void Append(GrBatch* origBatch,
229 GrColor color,
230 const SkMatrix& viewMatrix,
231 const SkRect& rect,
232 SkScalar strokeWidth,
233 bool snapToPixelCenters) {
234 NonAAStrokeRectBatch* batch = origBatch->cast<NonAAStrokeRectBatch>();
235 batch->appendAndUpdateBounds(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
joshualitt3566d442015-09-18 07:12:55 -0700236}
237
238};
239
240#ifdef GR_TEST_UTILS
241
242DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
243 SkMatrix viewMatrix = GrTest::TestMatrix(random);
244 GrColor color = GrRandomColor(random);
245 SkRect rect = GrTest::TestRect(random);
246 SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f;
247
joshualittaa37a962015-09-18 13:03:25 -0700248 return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
joshualitt3566d442015-09-18 07:12:55 -0700249}
250
251#endif