blob: f443b32f19f8d2313b895a7e732357995541c547 [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
bsalomon28ef3962016-07-06 18:56:04 -070040// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
41inline static bool allowed_stroke(const SkStrokeRec& stroke) {
42 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
43 stroke.getStyle() == SkStrokeRec::kHairline_Style);
44 return !stroke.getWidth() ||
45 (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
46}
47
joshualitt3566d442015-09-18 07:12:55 -070048class NonAAStrokeRectBatch : public GrVertexBatch {
49public:
50 DEFINE_BATCH_CLASS_ID
51
bsalomon28ef3962016-07-06 18:56:04 -070052 const char* name() const override { return "NonAAStrokeRectBatch"; }
joshualitt3566d442015-09-18 07:12:55 -070053
halcanary9d524f22016-03-29 09:03:52 -070054 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -080055 GrInitInvariantOutput* coverage,
56 GrBatchToXPOverrides* overrides) const override {
joshualitt3566d442015-09-18 07:12:55 -070057 // When this is called on a batch, there is only one geometry bundle
bsalomon28ef3962016-07-06 18:56:04 -070058 color->setKnownFourComponents(fColor);
ethannicholasff210322015-11-24 12:10:10 -080059 coverage->setKnownSingleComponent(0xff);
joshualitt3566d442015-09-18 07:12:55 -070060 }
61
bsalomon28ef3962016-07-06 18:56:04 -070062 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
63 const SkStrokeRec& stroke, bool snapToPixelCenters) {
64 if (!allowed_stroke(stroke)) {
65 return nullptr;
66 }
67 NonAAStrokeRectBatch* batch = new NonAAStrokeRectBatch();
68 batch->fColor = color;
69 batch->fViewMatrix = viewMatrix;
70 batch->fRect = rect;
joshualitt144c3c82015-11-30 12:30:13 -080071 // Sort the rect for hairlines
bsalomon28ef3962016-07-06 18:56:04 -070072 batch->fRect.sort();
73 batch->fStrokeWidth = stroke.getWidth();
joshualittaa37a962015-09-18 13:03:25 -070074
bsalomon28ef3962016-07-06 18:56:04 -070075 SkScalar rad = SkScalarHalf(batch->fStrokeWidth);
bsalomon88cf17d2016-07-08 06:40:56 -070076 SkRect bounds = rect;
77 bounds.outset(rad, rad);
joshualittaa37a962015-09-18 13:03:25 -070078
79 // If our caller snaps to pixel centers then we have to round out the bounds
80 if (snapToPixelCenters) {
bsalomon88cf17d2016-07-08 06:40:56 -070081 viewMatrix.mapRect(&bounds);
egdaniel7e8cc212016-07-06 14:38:34 -070082 // We want to be consistent with how we snap non-aa lines. To match what we do in
83 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
84 // pixel to force us to pixel centers.
bsalomon88cf17d2016-07-08 06:40:56 -070085 bounds.set(SkScalarFloorToScalar(bounds.fLeft),
86 SkScalarFloorToScalar(bounds.fTop),
87 SkScalarFloorToScalar(bounds.fRight),
88 SkScalarFloorToScalar(bounds.fBottom));
89 bounds.offset(0.5f, 0.5f);
90 batch->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
91 } else {
92 batch->setTransformedBounds(bounds, batch->fViewMatrix, HasAABloat ::kNo,
93 IsZeroArea::kNo);
joshualittaa37a962015-09-18 13:03:25 -070094 }
bsalomon28ef3962016-07-06 18:56:04 -070095 return batch;
joshualittaa37a962015-09-18 13:03:25 -070096 }
97
bsalomon28ef3962016-07-06 18:56:04 -070098private:
99 NonAAStrokeRectBatch() : INHERITED(ClassID()) {}
100
joshualitt144c3c82015-11-30 12:30:13 -0800101 void onPrepareDraws(Target* target) const override {
bungeman06ca8ec2016-06-09 08:01:03 -0700102 sk_sp<GrGeometryProcessor> gp;
joshualitt3566d442015-09-18 07:12:55 -0700103 {
104 using namespace GrDefaultGeoProcFactory;
bsalomon28ef3962016-07-06 18:56:04 -0700105 Color color(fColor);
106 Coverage coverage(fOverrides.readsCoverage() ? Coverage::kSolid_Type
107 : Coverage::kNone_Type);
108 LocalCoords localCoords(fOverrides.readsLocalCoords() ? LocalCoords::kUsePosition_Type :
109 LocalCoords::kUnused_Type);
110 gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix);
joshualitt3566d442015-09-18 07:12:55 -0700111 }
112
joshualitt3566d442015-09-18 07:12:55 -0700113 size_t vertexStride = gp->getVertexStride();
114
115 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
116
joshualitt3566d442015-09-18 07:12:55 -0700117 int vertexCount = kVertsPerHairlineRect;
bsalomon28ef3962016-07-06 18:56:04 -0700118 if (fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700119 vertexCount = kVertsPerStrokeRect;
120 }
121
cdalton397536c2016-03-25 12:15:03 -0700122 const GrBuffer* vertexBuffer;
joshualitt3566d442015-09-18 07:12:55 -0700123 int firstVertex;
124
125 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
126 &firstVertex);
127
128 if (!verts) {
129 SkDebugf("Could not allocate vertices\n");
130 return;
131 }
132
133 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
134
135 GrPrimitiveType primType;
bsalomon28ef3962016-07-06 18:56:04 -0700136 if (fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700137 primType = kTriangleStrip_GrPrimitiveType;
bsalomon28ef3962016-07-06 18:56:04 -0700138 init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
joshualitt3566d442015-09-18 07:12:55 -0700139 } else {
140 // hairline
141 primType = kLineStrip_GrPrimitiveType;
bsalomon28ef3962016-07-06 18:56:04 -0700142 vertex[0].set(fRect.fLeft, fRect.fTop);
143 vertex[1].set(fRect.fRight, fRect.fTop);
144 vertex[2].set(fRect.fRight, fRect.fBottom);
145 vertex[3].set(fRect.fLeft, fRect.fBottom);
146 vertex[4].set(fRect.fLeft, fRect.fTop);
joshualitt3566d442015-09-18 07:12:55 -0700147 }
148
egdaniel0e1853c2016-03-17 11:35:45 -0700149 GrMesh mesh;
150 mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
bungeman06ca8ec2016-06-09 08:01:03 -0700151 target->draw(gp.get(), mesh);
joshualitt3566d442015-09-18 07:12:55 -0700152 }
153
ethannicholasff210322015-11-24 12:10:10 -0800154 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomon28ef3962016-07-06 18:56:04 -0700155 overrides.getOverrideColorIfSet(&fColor);
156 fOverrides = overrides;
joshualitt3566d442015-09-18 07:12:55 -0700157 }
158
joshualitt3566d442015-09-18 07:12:55 -0700159 bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
joshualitt3566d442015-09-18 07:12:55 -0700160 // NonAA stroke rects cannot batch right now
161 // TODO make these batchable
162 return false;
163 }
164
bsalomon28ef3962016-07-06 18:56:04 -0700165 GrColor fColor;
166 SkMatrix fViewMatrix;
167 SkRect fRect;
168 SkScalar fStrokeWidth;
169
170 GrXPOverridesForBatch fOverrides;
joshualitt3566d442015-09-18 07:12:55 -0700171
172 const static int kVertsPerHairlineRect = 5;
173 const static int kVertsPerStrokeRect = 10;
174
joshualitt3566d442015-09-18 07:12:55 -0700175
176 typedef GrVertexBatch INHERITED;
177};
178
179namespace GrNonAAStrokeRectBatch {
180
181GrDrawBatch* Create(GrColor color,
182 const SkMatrix& viewMatrix,
183 const SkRect& rect,
bsalomona7d85ba2016-07-06 11:54:59 -0700184 const SkStrokeRec& stroke,
joshualitt3566d442015-09-18 07:12:55 -0700185 bool snapToPixelCenters) {
bsalomon28ef3962016-07-06 18:56:04 -0700186 return NonAAStrokeRectBatch::Create(color, viewMatrix, rect, stroke, snapToPixelCenters);
joshualittaa37a962015-09-18 13:03:25 -0700187}
188
bsalomon28ef3962016-07-06 18:56:04 -0700189}
joshualitt3566d442015-09-18 07:12:55 -0700190
191#ifdef GR_TEST_UTILS
192
193DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
194 SkMatrix viewMatrix = GrTest::TestMatrix(random);
195 GrColor color = GrRandomColor(random);
196 SkRect rect = GrTest::TestRect(random);
bsalomona7d85ba2016-07-06 11:54:59 -0700197 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
198 SkPaint paint;
199 paint.setStrokeWidth(strokeWidth);
200 paint.setStyle(SkPaint::kStroke_Style);
201 paint.setStrokeJoin(SkPaint::kMiter_Join);
202 SkStrokeRec strokeRec(paint);
203 return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeRec, random->nextBool());
joshualitt3566d442015-09-18 07:12:55 -0700204}
205
206#endif