blob: badcf070f443895bab1e954aaf4085cbbc073e35 [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);
joshualitt3566d442015-09-18 07:12:55 -070025
26 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
27 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
28 verts[2].set(rect.fRight - rad, rect.fTop + rad);
29 verts[3].set(rect.fRight + rad, rect.fTop - rad);
30 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
31 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
32 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
33 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
34 verts[8] = verts[0];
35 verts[9] = verts[1];
Robert Phillips621ee982017-08-04 15:35:00 -040036
37 // TODO: we should be catching this higher up the call stack and just draw a single
38 // non-AA rect
39 if (2*rad >= rect.width()) {
40 verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX();
41 }
42 if (2*rad >= rect.height()) {
43 verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY();
44 }
joshualitt3566d442015-09-18 07:12:55 -070045}
46
bsalomon28ef3962016-07-06 18:56:04 -070047// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
48inline static bool allowed_stroke(const SkStrokeRec& stroke) {
49 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
50 stroke.getStyle() == SkStrokeRec::kHairline_Style);
51 return !stroke.getWidth() ||
52 (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
53}
54
Brian Salomonbaaf4392017-06-15 09:59:23 -040055namespace {
56
57class NonAAStrokeRectOp final : public GrMeshDrawOp {
58private:
59 using Helper = GrSimpleMeshDrawOpHelper;
60
joshualitt3566d442015-09-18 07:12:55 -070061public:
Brian Salomon25a88092016-12-01 09:36:50 -050062 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -070063
Brian Salomon53e4c3c2016-12-21 11:38:53 -050064 const char* name() const override { return "NonAAStrokeRectOp"; }
joshualitt3566d442015-09-18 07:12:55 -070065
Brian Salomon7c3e7182016-12-01 09:35:30 -050066 SkString dumpInfo() const override {
67 SkString string;
Brian Salomon6a639042016-12-14 11:08:17 -050068 string.appendf(
69 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
70 "StrokeWidth: %.2f\n",
71 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
Brian Salomon82dfd3d2017-06-14 12:30:35 -040072 string += fHelper.dumpInfo();
73 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -050074 return string;
75 }
76
Brian Salomonbaaf4392017-06-15 09:59:23 -040077 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
78 const SkRect& rect, const SkStrokeRec& stroke,
79 GrAAType aaType) {
bsalomon28ef3962016-07-06 18:56:04 -070080 if (!allowed_stroke(stroke)) {
81 return nullptr;
82 }
Brian Salomonbaaf4392017-06-15 09:59:23 -040083 Helper::Flags flags = Helper::Flags::kNone;
84 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
85 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
86 // when MSAA is enabled because it can cause ugly artifacts.
87 if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
88 flags |= Helper::Flags::kSnapVerticesToPixelCenters;
89 }
90 return Helper::FactoryHelper<NonAAStrokeRectOp>(std::move(paint), flags, viewMatrix, rect,
91 stroke, aaType);
92 }
Brian Salomon1ec03f32017-06-14 09:49:26 -040093
Brian Salomonbaaf4392017-06-15 09:59:23 -040094 NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, Helper::Flags flags,
95 const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
96 GrAAType aaType)
97 : INHERITED(ClassID()), fHelper(helperArgs, aaType, flags) {
98 fColor = color;
99 fViewMatrix = viewMatrix;
100 fRect = rect;
101 // Sort the rect for hairlines
102 fRect.sort();
103 fStrokeWidth = stroke.getWidth();
104
105 SkScalar rad = SkScalarHalf(fStrokeWidth);
bsalomon88cf17d2016-07-08 06:40:56 -0700106 SkRect bounds = rect;
107 bounds.outset(rad, rad);
joshualittaa37a962015-09-18 13:03:25 -0700108
109 // If our caller snaps to pixel centers then we have to round out the bounds
Brian Salomonbaaf4392017-06-15 09:59:23 -0400110 if (flags & Helper::Flags::kSnapVerticesToPixelCenters) {
bsalomon88cf17d2016-07-08 06:40:56 -0700111 viewMatrix.mapRect(&bounds);
egdaniel7e8cc212016-07-06 14:38:34 -0700112 // We want to be consistent with how we snap non-aa lines. To match what we do in
113 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
114 // pixel to force us to pixel centers.
bsalomon88cf17d2016-07-08 06:40:56 -0700115 bounds.set(SkScalarFloorToScalar(bounds.fLeft),
116 SkScalarFloorToScalar(bounds.fTop),
117 SkScalarFloorToScalar(bounds.fRight),
118 SkScalarFloorToScalar(bounds.fBottom));
119 bounds.offset(0.5f, 0.5f);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400120 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -0700121 } else {
Brian Salomonbaaf4392017-06-15 09:59:23 -0400122 this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
joshualittaa37a962015-09-18 13:03:25 -0700123 }
Brian Salomonbaaf4392017-06-15 09:59:23 -0400124 }
125
126 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
127
Brian Salomonf86d37b2017-06-16 10:04:34 -0400128 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
129 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone,
130 &fColor);
joshualittaa37a962015-09-18 13:03:25 -0700131 }
132
bsalomon28ef3962016-07-06 18:56:04 -0700133private:
Brian Salomon91326c32017-08-09 16:02:19 -0400134 void onPrepareDraws(Target* target) override {
bungeman06ca8ec2016-06-09 08:01:03 -0700135 sk_sp<GrGeometryProcessor> gp;
joshualitt3566d442015-09-18 07:12:55 -0700136 {
137 using namespace GrDefaultGeoProcFactory;
bsalomon28ef3962016-07-06 18:56:04 -0700138 Color color(fColor);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400139 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
Brian Salomon8c852be2017-01-04 10:44:42 -0500140 ? LocalCoords::kUsePosition_Type
141 : LocalCoords::kUnused_Type;
142 gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
143 fViewMatrix);
joshualitt3566d442015-09-18 07:12:55 -0700144 }
145
joshualitt3566d442015-09-18 07:12:55 -0700146 size_t vertexStride = gp->getVertexStride();
147
148 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
149
joshualitt3566d442015-09-18 07:12:55 -0700150 int vertexCount = kVertsPerHairlineRect;
bsalomon28ef3962016-07-06 18:56:04 -0700151 if (fStrokeWidth > 0) {
joshualitt3566d442015-09-18 07:12:55 -0700152 vertexCount = kVertsPerStrokeRect;
153 }
154
cdalton397536c2016-03-25 12:15:03 -0700155 const GrBuffer* vertexBuffer;
joshualitt3566d442015-09-18 07:12:55 -0700156 int firstVertex;
157
Brian Salomon6a639042016-12-14 11:08:17 -0500158 void* verts =
159 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
joshualitt3566d442015-09-18 07:12:55 -0700160
161 if (!verts) {
162 SkDebugf("Could not allocate vertices\n");
163 return;
164 }
165
166 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
167
168 GrPrimitiveType primType;
bsalomon28ef3962016-07-06 18:56:04 -0700169 if (fStrokeWidth > 0) {
Chris Dalton3809bab2017-06-13 10:55:06 -0600170 primType = GrPrimitiveType::kTriangleStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700171 init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
joshualitt3566d442015-09-18 07:12:55 -0700172 } else {
173 // hairline
Chris Dalton3809bab2017-06-13 10:55:06 -0600174 primType = GrPrimitiveType::kLineStrip;
bsalomon28ef3962016-07-06 18:56:04 -0700175 vertex[0].set(fRect.fLeft, fRect.fTop);
176 vertex[1].set(fRect.fRight, fRect.fTop);
177 vertex[2].set(fRect.fRight, fRect.fBottom);
178 vertex[3].set(fRect.fLeft, fRect.fBottom);
179 vertex[4].set(fRect.fLeft, fRect.fTop);
joshualitt3566d442015-09-18 07:12:55 -0700180 }
181
Chris Daltonbca46e22017-05-15 11:03:26 -0600182 GrMesh mesh(primType);
Chris Dalton1d616352017-05-31 12:51:23 -0600183 mesh.setNonIndexedNonInstanced(vertexCount);
Chris Dalton114a3c02017-05-26 15:17:19 -0600184 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400185 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt3566d442015-09-18 07:12:55 -0700186 }
187
Brian Salomon25a88092016-12-01 09:36:50 -0500188 bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500189 // NonAA stroke rects cannot combine right now
190 // TODO make these combinable.
joshualitt3566d442015-09-18 07:12:55 -0700191 return false;
192 }
193
Brian Salomonbaaf4392017-06-15 09:59:23 -0400194 Helper fHelper;
bsalomon28ef3962016-07-06 18:56:04 -0700195 GrColor fColor;
196 SkMatrix fViewMatrix;
197 SkRect fRect;
198 SkScalar fStrokeWidth;
joshualitt3566d442015-09-18 07:12:55 -0700199
200 const static int kVertsPerHairlineRect = 5;
201 const static int kVertsPerStrokeRect = 10;
202
Brian Salomonbaaf4392017-06-15 09:59:23 -0400203 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700204};
205
Brian Salomonbaaf4392017-06-15 09:59:23 -0400206} // anonymous namespace
joshualitt3566d442015-09-18 07:12:55 -0700207
Brian Salomonbaaf4392017-06-15 09:59:23 -0400208namespace GrRectOpFactory {
209std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&& paint,
210 const SkMatrix& viewMatrix,
211 const SkRect& rect,
212 const SkStrokeRec& stroke,
213 GrAAType aaType) {
214 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke, aaType);
joshualittaa37a962015-09-18 13:03:25 -0700215}
Brian Salomonbaaf4392017-06-15 09:59:23 -0400216} // namespace GrRectOpFactory
joshualitt3566d442015-09-18 07:12:55 -0700217
Hal Canary6f6961e2017-01-31 13:50:44 -0500218#if GR_TEST_UTILS
joshualitt3566d442015-09-18 07:12:55 -0700219
Brian Salomonbaaf4392017-06-15 09:59:23 -0400220GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
joshualitt3566d442015-09-18 07:12:55 -0700221 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt3566d442015-09-18 07:12:55 -0700222 SkRect rect = GrTest::TestRect(random);
bsalomona7d85ba2016-07-06 11:54:59 -0700223 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
Brian Salomonbaaf4392017-06-15 09:59:23 -0400224 SkPaint strokePaint;
225 strokePaint.setStrokeWidth(strokeWidth);
226 strokePaint.setStyle(SkPaint::kStroke_Style);
227 strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
228 SkStrokeRec strokeRec(strokePaint);
229 GrAAType aaType = GrAAType::kNone;
230 if (fsaaType == GrFSAAType::kUnifiedMSAA) {
231 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
232 }
233 return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, strokeRec, aaType);
joshualitt3566d442015-09-18 07:12:55 -0700234}
235
236#endif