blob: 2c3d822705cafde578867da104cd1f97ced0b0ab [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
joshualitt3566d442015-09-18 07:12:55 -07008#include "GrColor.h"
9#include "GrDefaultGeoProcFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050010#include "GrDrawOpTest.h"
Brian Salomondad29232016-12-01 16:40:24 -050011#include "GrMeshDrawOp.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050012#include "GrOpFlushState.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040013#include "GrRectOpFactory.h"
14#include "GrSimpleMeshDrawOpHelper.h"
Brian Salomona0485d92017-06-14 19:08:01 -040015#include "SkRandom.h"
Brian Salomonbaaf4392017-06-15 09:59:23 -040016#include "SkStrokeRec.h"
joshualitt3566d442015-09-18 07:12:55 -070017
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 Salomonbaaf4392017-06-15 09:59:23 -040049namespace {
50
51class NonAAStrokeRectOp final : public GrMeshDrawOp {
52private:
53 using Helper = GrSimpleMeshDrawOpHelper;
54
joshualitt3566d442015-09-18 07:12:55 -070055public:
Brian Salomon25a88092016-12-01 09:36:50 -050056 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -070057
Brian Salomon53e4c3c2016-12-21 11:38:53 -050058 const char* name() const override { return "NonAAStrokeRectOp"; }
joshualitt3566d442015-09-18 07:12:55 -070059
Brian Salomon7c3e7182016-12-01 09:35:30 -050060 SkString dumpInfo() const override {
61 SkString string;
Brian Salomon6a639042016-12-14 11:08:17 -050062 string.appendf(
63 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
64 "StrokeWidth: %.2f\n",
65 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
Brian Salomon82dfd3d2017-06-14 12:30:35 -040066 string += fHelper.dumpInfo();
67 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -050068 return string;
69 }
70
Brian Salomonbaaf4392017-06-15 09:59:23 -040071 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
72 const SkRect& rect, const SkStrokeRec& stroke,
73 GrAAType aaType) {
bsalomon28ef3962016-07-06 18:56:04 -070074 if (!allowed_stroke(stroke)) {
75 return nullptr;
76 }
Brian Salomonbaaf4392017-06-15 09:59:23 -040077 Helper::Flags flags = Helper::Flags::kNone;
78 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
79 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
80 // when MSAA is enabled because it can cause ugly artifacts.
81 if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
82 flags |= Helper::Flags::kSnapVerticesToPixelCenters;
83 }
84 return Helper::FactoryHelper<NonAAStrokeRectOp>(std::move(paint), flags, viewMatrix, rect,
85 stroke, aaType);
86 }
Brian Salomon1ec03f32017-06-14 09:49:26 -040087
Brian Salomonbaaf4392017-06-15 09:59:23 -040088 NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, Helper::Flags flags,
89 const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
90 GrAAType aaType)
91 : INHERITED(ClassID()), fHelper(helperArgs, aaType, flags) {
92 fColor = color;
93 fViewMatrix = viewMatrix;
94 fRect = rect;
95 // Sort the rect for hairlines
96 fRect.sort();
97 fStrokeWidth = stroke.getWidth();
98
99 SkScalar rad = SkScalarHalf(fStrokeWidth);
bsalomon88cf17d2016-07-08 06:40:56 -0700100 SkRect bounds = rect;
101 bounds.outset(rad, rad);
joshualittaa37a962015-09-18 13:03:25 -0700102
103 // If our caller snaps to pixel centers then we have to round out the bounds
Brian Salomonbaaf4392017-06-15 09:59:23 -0400104 if (flags & Helper::Flags::kSnapVerticesToPixelCenters) {
bsalomon88cf17d2016-07-08 06:40:56 -0700105 viewMatrix.mapRect(&bounds);
egdaniel7e8cc212016-07-06 14:38:34 -0700106 // We want to be consistent with how we snap non-aa lines. To match what we do in
107 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
108 // pixel to force us to pixel centers.
bsalomon88cf17d2016-07-08 06:40:56 -0700109 bounds.set(SkScalarFloorToScalar(bounds.fLeft),
110 SkScalarFloorToScalar(bounds.fTop),
111 SkScalarFloorToScalar(bounds.fRight),
112 SkScalarFloorToScalar(bounds.fBottom));
113 bounds.offset(0.5f, 0.5f);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400114 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -0700115 } else {
Brian Salomonbaaf4392017-06-15 09:59:23 -0400116 this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
joshualittaa37a962015-09-18 13:03:25 -0700117 }
Brian Salomonbaaf4392017-06-15 09:59:23 -0400118 }
119
120 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
121
Brian Salomonf86d37b2017-06-16 10:04:34 -0400122 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
123 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone,
124 &fColor);
joshualittaa37a962015-09-18 13:03:25 -0700125 }
126
bsalomon28ef3962016-07-06 18:56:04 -0700127private:
joshualitt144c3c82015-11-30 12:30:13 -0800128 void onPrepareDraws(Target* target) const override {
bungeman06ca8ec2016-06-09 08:01:03 -0700129 sk_sp<GrGeometryProcessor> gp;
joshualitt3566d442015-09-18 07:12:55 -0700130 {
131 using namespace GrDefaultGeoProcFactory;
bsalomon28ef3962016-07-06 18:56:04 -0700132 Color color(fColor);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400133 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
Brian Salomon8c852be2017-01-04 10:44:42 -0500134 ? LocalCoords::kUsePosition_Type
135 : LocalCoords::kUnused_Type;
136 gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
137 fViewMatrix);
joshualitt3566d442015-09-18 07:12:55 -0700138 }
139
joshualitt3566d442015-09-18 07:12:55 -0700140 size_t vertexStride = gp->getVertexStride();
141
142 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
143
joshualitt3566d442015-09-18 07:12:55 -0700144 int vertexCount = kVertsPerHairlineRect;
bsalomon28ef3962016-07-06 18:56:04 -0700145 if (fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700146 vertexCount = kVertsPerStrokeRect;
147 }
148
cdalton397536c2016-03-25 12:15:03 -0700149 const GrBuffer* vertexBuffer;
joshualitt3566d442015-09-18 07:12:55 -0700150 int firstVertex;
151
Brian Salomon6a639042016-12-14 11:08:17 -0500152 void* verts =
153 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
joshualitt3566d442015-09-18 07:12:55 -0700154
155 if (!verts) {
156 SkDebugf("Could not allocate vertices\n");
157 return;
158 }
159
160 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
161
162 GrPrimitiveType primType;
bsalomon28ef3962016-07-06 18:56:04 -0700163 if (fStrokeWidth > 0) {
Chris Dalton3809bab2017-06-13 10:55:06 -0600164 primType = GrPrimitiveType::kTriangleStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700165 init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
joshualitt3566d442015-09-18 07:12:55 -0700166 } else {
167 // hairline
Chris Dalton3809bab2017-06-13 10:55:06 -0600168 primType = GrPrimitiveType::kLineStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700169 vertex[0].set(fRect.fLeft, fRect.fTop);
170 vertex[1].set(fRect.fRight, fRect.fTop);
171 vertex[2].set(fRect.fRight, fRect.fBottom);
172 vertex[3].set(fRect.fLeft, fRect.fBottom);
173 vertex[4].set(fRect.fLeft, fRect.fTop);
joshualitt3566d442015-09-18 07:12:55 -0700174 }
175
Chris Daltonbca46e22017-05-15 11:03:26 -0600176 GrMesh mesh(primType);
Chris Dalton1d616352017-05-31 12:51:23 -0600177 mesh.setNonIndexedNonInstanced(vertexCount);
Chris Dalton114a3c02017-05-26 15:17:19 -0600178 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400179 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt3566d442015-09-18 07:12:55 -0700180 }
181
Brian Salomon25a88092016-12-01 09:36:50 -0500182 bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500183 // NonAA stroke rects cannot combine right now
184 // TODO make these combinable.
joshualitt3566d442015-09-18 07:12:55 -0700185 return false;
186 }
187
Brian Salomonbaaf4392017-06-15 09:59:23 -0400188 Helper fHelper;
bsalomon28ef3962016-07-06 18:56:04 -0700189 GrColor fColor;
190 SkMatrix fViewMatrix;
191 SkRect fRect;
192 SkScalar fStrokeWidth;
joshualitt3566d442015-09-18 07:12:55 -0700193
194 const static int kVertsPerHairlineRect = 5;
195 const static int kVertsPerStrokeRect = 10;
196
Brian Salomonbaaf4392017-06-15 09:59:23 -0400197 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700198};
199
Brian Salomonbaaf4392017-06-15 09:59:23 -0400200} // anonymous namespace
joshualitt3566d442015-09-18 07:12:55 -0700201
Brian Salomonbaaf4392017-06-15 09:59:23 -0400202namespace GrRectOpFactory {
203std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&& paint,
204 const SkMatrix& viewMatrix,
205 const SkRect& rect,
206 const SkStrokeRec& stroke,
207 GrAAType aaType) {
208 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke, aaType);
joshualittaa37a962015-09-18 13:03:25 -0700209}
Brian Salomonbaaf4392017-06-15 09:59:23 -0400210} // namespace GrRectOpFactory
joshualitt3566d442015-09-18 07:12:55 -0700211
Hal Canary6f6961e2017-01-31 13:50:44 -0500212#if GR_TEST_UTILS
joshualitt3566d442015-09-18 07:12:55 -0700213
Brian Salomonbaaf4392017-06-15 09:59:23 -0400214GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
joshualitt3566d442015-09-18 07:12:55 -0700215 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt3566d442015-09-18 07:12:55 -0700216 SkRect rect = GrTest::TestRect(random);
bsalomona7d85ba2016-07-06 11:54:59 -0700217 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
Brian Salomonbaaf4392017-06-15 09:59:23 -0400218 SkPaint strokePaint;
219 strokePaint.setStrokeWidth(strokeWidth);
220 strokePaint.setStyle(SkPaint::kStroke_Style);
221 strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
222 SkStrokeRec strokeRec(strokePaint);
223 GrAAType aaType = GrAAType::kNone;
224 if (fsaaType == GrFSAAType::kUnifiedMSAA) {
225 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
226 }
227 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, strokeRec, aaType);
joshualitt3566d442015-09-18 07:12:55 -0700228}
229
230#endif