blob: 94dc373fc68aca3ddd3bc4f00d2ae1391585ecfc [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
Brian Salomon6a639042016-12-14 11:08:17 -05008#include "GrNonAAStrokeRectOp.h"
joshualitt3566d442015-09-18 07:12:55 -07009
joshualitt3566d442015-09-18 07:12:55 -070010#include "GrColor.h"
11#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050012#include "GrDrawOpTest.h"
Brian Salomondad29232016-12-01 16:40:24 -050013#include "GrMeshDrawOp.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050014#include "GrOpFlushState.h"
Hal Canary6f6961e2017-01-31 13:50:44 -050015#include "SkStrokeRec.h"
joshualitt3566d442015-09-18 07:12:55 -070016#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 */
23static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
24 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
Brian Salomon6a639042016-12-14 11:08:17 -050027 // SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
joshualitt3566d442015-09-18 07:12:55 -070028
29 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
30 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
31 verts[2].set(rect.fRight - rad, rect.fTop + rad);
32 verts[3].set(rect.fRight + rad, rect.fTop - rad);
33 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
34 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
35 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
36 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
37 verts[8] = verts[0];
38 verts[9] = verts[1];
39}
40
bsalomon28ef3962016-07-06 18:56:04 -070041// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
42inline static bool allowed_stroke(const SkStrokeRec& stroke) {
43 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
44 stroke.getStyle() == SkStrokeRec::kHairline_Style);
45 return !stroke.getWidth() ||
46 (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
47}
48
Brian Salomond3ccb0a2017-04-03 10:38:00 -040049class NonAAStrokeRectOp final : public GrLegacyMeshDrawOp {
joshualitt3566d442015-09-18 07:12:55 -070050public:
Brian Salomon25a88092016-12-01 09:36:50 -050051 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -070052
Brian Salomon53e4c3c2016-12-21 11:38:53 -050053 const char* name() const override { return "NonAAStrokeRectOp"; }
joshualitt3566d442015-09-18 07:12:55 -070054
Brian Salomon7c3e7182016-12-01 09:35:30 -050055 SkString dumpInfo() const override {
56 SkString string;
Brian Salomon6a639042016-12-14 11:08:17 -050057 string.appendf(
58 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
59 "StrokeWidth: %.2f\n",
60 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
Brian Salomon7c3e7182016-12-01 09:35:30 -050061 string.append(DumpPipelineInfo(*this->pipeline()));
62 string.append(INHERITED::dumpInfo());
63 return string;
64 }
65
Brian Salomond3ccb0a2017-04-03 10:38:00 -040066 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
67 const SkRect& rect, const SkStrokeRec& stroke,
68 bool snapToPixelCenters) {
bsalomon28ef3962016-07-06 18:56:04 -070069 if (!allowed_stroke(stroke)) {
70 return nullptr;
71 }
Brian Salomon6a639042016-12-14 11:08:17 -050072 NonAAStrokeRectOp* op = new NonAAStrokeRectOp();
73 op->fColor = color;
74 op->fViewMatrix = viewMatrix;
75 op->fRect = rect;
joshualitt144c3c82015-11-30 12:30:13 -080076 // Sort the rect for hairlines
Brian Salomon6a639042016-12-14 11:08:17 -050077 op->fRect.sort();
78 op->fStrokeWidth = stroke.getWidth();
joshualittaa37a962015-09-18 13:03:25 -070079
Brian Salomon6a639042016-12-14 11:08:17 -050080 SkScalar rad = SkScalarHalf(op->fStrokeWidth);
bsalomon88cf17d2016-07-08 06:40:56 -070081 SkRect bounds = rect;
82 bounds.outset(rad, rad);
joshualittaa37a962015-09-18 13:03:25 -070083
84 // If our caller snaps to pixel centers then we have to round out the bounds
85 if (snapToPixelCenters) {
bsalomon88cf17d2016-07-08 06:40:56 -070086 viewMatrix.mapRect(&bounds);
egdaniel7e8cc212016-07-06 14:38:34 -070087 // 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.
bsalomon88cf17d2016-07-08 06:40:56 -070090 bounds.set(SkScalarFloorToScalar(bounds.fLeft),
91 SkScalarFloorToScalar(bounds.fTop),
92 SkScalarFloorToScalar(bounds.fRight),
93 SkScalarFloorToScalar(bounds.fBottom));
94 bounds.offset(0.5f, 0.5f);
Brian Salomon6a639042016-12-14 11:08:17 -050095 op->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -070096 } else {
Brian Salomon6a639042016-12-14 11:08:17 -050097 op->setTransformedBounds(bounds, op->fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
joshualittaa37a962015-09-18 13:03:25 -070098 }
Brian Salomond3ccb0a2017-04-03 10:38:00 -040099 return std::unique_ptr<GrLegacyMeshDrawOp>(op);
joshualittaa37a962015-09-18 13:03:25 -0700100 }
101
bsalomon28ef3962016-07-06 18:56:04 -0700102private:
Brian Salomon6a639042016-12-14 11:08:17 -0500103 NonAAStrokeRectOp() : INHERITED(ClassID()) {}
bsalomon28ef3962016-07-06 18:56:04 -0700104
Brian Salomona811b122017-03-30 08:21:32 -0400105 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
106 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -0400107 color->setToConstant(fColor);
Brian Salomona811b122017-03-30 08:21:32 -0400108 *coverage = GrProcessorAnalysisCoverage::kNone;
Brian Salomon92aee3d2016-12-21 09:20:25 -0500109 }
110
joshualitt144c3c82015-11-30 12:30:13 -0800111 void onPrepareDraws(Target* target) const override {
bungeman06ca8ec2016-06-09 08:01:03 -0700112 sk_sp<GrGeometryProcessor> gp;
joshualitt3566d442015-09-18 07:12:55 -0700113 {
114 using namespace GrDefaultGeoProcFactory;
bsalomon28ef3962016-07-06 18:56:04 -0700115 Color color(fColor);
Brian Salomonecea86a2017-01-04 13:25:17 -0500116 LocalCoords::Type localCoordsType = fNeedsLocalCoords
Brian Salomon8c852be2017-01-04 10:44:42 -0500117 ? LocalCoords::kUsePosition_Type
118 : LocalCoords::kUnused_Type;
119 gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
120 fViewMatrix);
joshualitt3566d442015-09-18 07:12:55 -0700121 }
122
joshualitt3566d442015-09-18 07:12:55 -0700123 size_t vertexStride = gp->getVertexStride();
124
125 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
126
joshualitt3566d442015-09-18 07:12:55 -0700127 int vertexCount = kVertsPerHairlineRect;
bsalomon28ef3962016-07-06 18:56:04 -0700128 if (fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700129 vertexCount = kVertsPerStrokeRect;
130 }
131
cdalton397536c2016-03-25 12:15:03 -0700132 const GrBuffer* vertexBuffer;
joshualitt3566d442015-09-18 07:12:55 -0700133 int firstVertex;
134
Brian Salomon6a639042016-12-14 11:08:17 -0500135 void* verts =
136 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
joshualitt3566d442015-09-18 07:12:55 -0700137
138 if (!verts) {
139 SkDebugf("Could not allocate vertices\n");
140 return;
141 }
142
143 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
144
145 GrPrimitiveType primType;
bsalomon28ef3962016-07-06 18:56:04 -0700146 if (fStrokeWidth > 0) {
Chris Dalton3809bab2017-06-13 10:55:06 -0600147 primType = GrPrimitiveType::kTriangleStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700148 init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
joshualitt3566d442015-09-18 07:12:55 -0700149 } else {
150 // hairline
Chris Dalton3809bab2017-06-13 10:55:06 -0600151 primType = GrPrimitiveType::kLineStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700152 vertex[0].set(fRect.fLeft, fRect.fTop);
153 vertex[1].set(fRect.fRight, fRect.fTop);
154 vertex[2].set(fRect.fRight, fRect.fBottom);
155 vertex[3].set(fRect.fLeft, fRect.fBottom);
156 vertex[4].set(fRect.fLeft, fRect.fTop);
joshualitt3566d442015-09-18 07:12:55 -0700157 }
158
Chris Daltonbca46e22017-05-15 11:03:26 -0600159 GrMesh mesh(primType);
Chris Dalton1d616352017-05-31 12:51:23 -0600160 mesh.setNonIndexedNonInstanced(vertexCount);
Chris Dalton114a3c02017-05-26 15:17:19 -0600161 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400162 target->draw(gp.get(), this->pipeline(), mesh);
joshualitt3566d442015-09-18 07:12:55 -0700163 }
164
Brian Salomone7d30482017-03-29 12:09:15 -0400165 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -0500166 optimizations.getOverrideColorIfSet(&fColor);
Brian Salomonecea86a2017-01-04 13:25:17 -0500167 fNeedsLocalCoords = optimizations.readsLocalCoords();
joshualitt3566d442015-09-18 07:12:55 -0700168 }
169
Brian Salomon25a88092016-12-01 09:36:50 -0500170 bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500171 // NonAA stroke rects cannot combine right now
172 // TODO make these combinable.
joshualitt3566d442015-09-18 07:12:55 -0700173 return false;
174 }
175
bsalomon28ef3962016-07-06 18:56:04 -0700176 GrColor fColor;
177 SkMatrix fViewMatrix;
178 SkRect fRect;
179 SkScalar fStrokeWidth;
Brian Salomonecea86a2017-01-04 13:25:17 -0500180 bool fNeedsLocalCoords;
joshualitt3566d442015-09-18 07:12:55 -0700181
182 const static int kVertsPerHairlineRect = 5;
183 const static int kVertsPerStrokeRect = 10;
184
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400185 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700186};
187
Brian Salomon6a639042016-12-14 11:08:17 -0500188namespace GrNonAAStrokeRectOp {
joshualitt3566d442015-09-18 07:12:55 -0700189
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400190std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
191 const SkMatrix& viewMatrix,
192 const SkRect& rect,
193 const SkStrokeRec& stroke,
194 bool snapToPixelCenters) {
Brian Salomon6a639042016-12-14 11:08:17 -0500195 return NonAAStrokeRectOp::Make(color, viewMatrix, rect, stroke, snapToPixelCenters);
joshualittaa37a962015-09-18 13:03:25 -0700196}
bsalomon28ef3962016-07-06 18:56:04 -0700197}
joshualitt3566d442015-09-18 07:12:55 -0700198
Hal Canary6f6961e2017-01-31 13:50:44 -0500199#if GR_TEST_UTILS
joshualitt3566d442015-09-18 07:12:55 -0700200
Brian Salomon17726632017-05-12 14:09:46 -0400201GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
joshualitt3566d442015-09-18 07:12:55 -0700202 SkMatrix viewMatrix = GrTest::TestMatrix(random);
203 GrColor color = GrRandomColor(random);
204 SkRect rect = GrTest::TestRect(random);
bsalomona7d85ba2016-07-06 11:54:59 -0700205 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
206 SkPaint paint;
207 paint.setStrokeWidth(strokeWidth);
208 paint.setStyle(SkPaint::kStroke_Style);
209 paint.setStrokeJoin(SkPaint::kMiter_Join);
210 SkStrokeRec strokeRec(paint);
Brian Salomon5ec9def2016-12-20 15:34:05 -0500211 return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, random->nextBool());
joshualitt3566d442015-09-18 07:12:55 -0700212}
213
214#endif