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 "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 */
init_stroke_rect_strip(SkPoint verts[10],const SkRect & rect,SkScalar width)22 static 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
40 class NonAAStrokeRectBatch : public GrVertexBatch {
41 public:
42 DEFINE_BATCH_CLASS_ID
43
44 struct Geometry {
45 SkMatrix fViewMatrix;
46 SkRect fRect;
47 SkScalar fStrokeWidth;
48 GrColor fColor;
49 };
50
Create()51 static NonAAStrokeRectBatch* Create() {
52 return new NonAAStrokeRectBatch;
53 }
54
name() const55 const char* name() const override { return "GrStrokeRectBatch"; }
56
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const57 void computePipelineOptimizations(GrInitInvariantOutput* color,
58 GrInitInvariantOutput* coverage,
59 GrBatchToXPOverrides* overrides) const override {
60 // When this is called on a batch, there is only one geometry bundle
61 color->setKnownFourComponents(fGeoData[0].fColor);
62 coverage->setKnownSingleComponent(0xff);
63 }
64
append(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth)65 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;
72
73 // Sort the rect for hairlines
74 geometry.fRect.sort();
75 }
76
appendAndUpdateBounds(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool snapToPixelCenters)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
init(bool snapToPixelCenters)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
94 private:
setupBounds(SkRect * bounds,const Geometry & geo,bool snapToPixelCenters)95 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
onPrepareDraws(Target * target) const107 void onPrepareDraws(Target* target) const override {
108 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
126 const Geometry& args = fGeoData[0];
127
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;
147 if (args.fStrokeWidth > 0) {;
148 primType = kTriangleStrip_GrPrimitiveType;
149 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
initBatchTracker(const GrXPOverridesForBatch & overrides)165 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
166 // Handle any color overrides
167 if (!overrides.readsColor()) {
168 fGeoData[0].fColor = GrColor_ILLEGAL;
169 }
170 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
171
172 // setup batch properties
173 fBatch.fColorIgnored = !overrides.readsColor();
174 fBatch.fColor = fGeoData[0].fColor;
175 fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
176 fBatch.fCoverageIgnored = !overrides.readsCoverage();
177 }
178
NonAAStrokeRectBatch()179 NonAAStrokeRectBatch() : INHERITED(ClassID()) {}
180
color() const181 GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const182 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
colorIgnored() const183 bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const184 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
hairline() const185 bool hairline() const { return fBatch.fHairline; }
coverageIgnored() const186 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
187
onCombineIfPossible(GrBatch * t,const GrCaps &)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
217 namespace GrNonAAStrokeRectBatch {
218
Create(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool snapToPixelCenters)219 GrDrawBatch* Create(GrColor color,
220 const SkMatrix& viewMatrix,
221 const SkRect& rect,
222 SkScalar strokeWidth,
223 bool snapToPixelCenters) {
224 NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create();
225 batch->append(color, viewMatrix, rect, strokeWidth);
226 batch->init(snapToPixelCenters);
227 return batch;
228 }
229
Append(GrBatch * origBatch,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,SkScalar strokeWidth,bool snapToPixelCenters)230 void 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);
238 }
239
240 };
241
242 #ifdef GR_TEST_UTILS
243
DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch)244 DRAW_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
250 return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
251 }
252
253 #endif
254