blob: a435832125e3d967f75421a0362f354e88564d3e [file] [log] [blame]
joshualitt9ff64252015-08-10 09:03:51 -07001/*
Michael Ludwig72ab3462018-12-10 12:43:36 -05002 * Copyright 2018 Google Inc.
joshualitt9ff64252015-08-10 09:03:51 -07003 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/ops/GrStrokeRectOp.h"
Michael Ludwig72ab3462018-12-10 12:43:36 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkStrokeRec.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/private/GrResourceKey.h"
12#include "include/utils/SkRandom.h"
13#include "src/gpu/GrCaps.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040014#include "src/gpu/GrColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "src/gpu/GrDefaultGeoProcFactory.h"
16#include "src/gpu/GrDrawOpTest.h"
17#include "src/gpu/GrOpFlushState.h"
18#include "src/gpu/GrResourceProvider.h"
19#include "src/gpu/GrVertexWriter.h"
20#include "src/gpu/ops/GrFillRectOp.h"
21#include "src/gpu/ops/GrMeshDrawOp.h"
22#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt9ff64252015-08-10 09:03:51 -070023
Michael Ludwig72ab3462018-12-10 12:43:36 -050024namespace {
joshualitt9ff64252015-08-10 09:03:51 -070025
bsalomon8b7a9e12016-07-06 13:06:22 -070026// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
Michael Ludwig72ab3462018-12-10 12:43:36 -050027// limit makes a miter join effectively beveled. If the miter is effectively beveled, it is only
28// supported when using an AA stroke.
29inline static bool allowed_stroke(const SkStrokeRec& stroke, GrAA aa, bool* isMiter) {
bsalomon8b7a9e12016-07-06 13:06:22 -070030 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
31 stroke.getStyle() == SkStrokeRec::kHairline_Style);
32 // For hairlines, make bevel and round joins appear the same as mitered ones.
33 if (!stroke.getWidth()) {
34 *isMiter = true;
35 return true;
36 }
37 if (stroke.getJoin() == SkPaint::kBevel_Join) {
38 *isMiter = false;
Michael Ludwig72ab3462018-12-10 12:43:36 -050039 return aa == GrAA::kYes; // bevel only supported with AA
bsalomon8b7a9e12016-07-06 13:06:22 -070040 }
41 if (stroke.getJoin() == SkPaint::kMiter_Join) {
42 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
Michael Ludwig72ab3462018-12-10 12:43:36 -050043 // Supported under non-AA only if it remains mitered
44 return aa == GrAA::kYes || *isMiter;
bsalomon8b7a9e12016-07-06 13:06:22 -070045 }
46 return false;
47}
48
Michael Ludwig72ab3462018-12-10 12:43:36 -050049
50///////////////////////////////////////////////////////////////////////////////////////////////////
51// Non-AA Stroking
52///////////////////////////////////////////////////////////////////////////////////////////////////
53
54/* create a triangle strip that strokes the specified rect. There are 8
55 unique vertices, but we repeat the last 2 to close up. Alternatively we
56 could use an indices array, and then only send 8 verts, but not sure that
57 would be faster.
58 */
59static void init_nonaa_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
60 const SkScalar rad = SkScalarHalf(width);
61
62 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
63 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
64 verts[2].set(rect.fRight - rad, rect.fTop + rad);
65 verts[3].set(rect.fRight + rad, rect.fTop - rad);
66 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
67 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
68 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
69 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
70 verts[8] = verts[0];
71 verts[9] = verts[1];
72
73 // TODO: we should be catching this higher up the call stack and just draw a single
74 // non-AA rect
75 if (2*rad >= rect.width()) {
76 verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX();
77 }
78 if (2*rad >= rect.height()) {
79 verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY();
80 }
81}
82
83class NonAAStrokeRectOp final : public GrMeshDrawOp {
84private:
85 using Helper = GrSimpleMeshDrawOpHelper;
86
87public:
88 DEFINE_OP_CLASS_ID
89
90 const char* name() const override { return "NonAAStrokeRectOp"; }
91
Chris Dalton1706cbf2019-05-21 19:35:29 -060092 void visitProxies(const VisitProxyFunc& func) const override {
Michael Ludwig72ab3462018-12-10 12:43:36 -050093 fHelper.visitProxies(func);
94 }
95
96#ifdef SK_DEBUG
97 SkString dumpInfo() const override {
98 SkString string;
99 string.appendf(
100 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
101 "StrokeWidth: %.2f\n",
102 fColor.toBytes_RGBA(), fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
103 fStrokeWidth);
104 string += fHelper.dumpInfo();
105 string += INHERITED::dumpInfo();
106 return string;
107 }
108#endif
109
Robert Phillipsb97da532019-02-12 15:24:12 -0500110 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500111 GrPaint&& paint,
112 const SkMatrix& viewMatrix,
113 const SkRect& rect,
114 const SkStrokeRec& stroke,
115 GrAAType aaType) {
116 bool isMiter;
117 if (!allowed_stroke(stroke, GrAA::kNo, &isMiter)) {
118 return nullptr;
119 }
Chris Daltonbaa1b352019-04-03 12:03:00 -0600120 Helper::InputFlags inputFlags = Helper::InputFlags::kNone;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500121 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
122 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
123 // when MSAA is enabled because it can cause ugly artifacts.
124 if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
Chris Daltonbaa1b352019-04-03 12:03:00 -0600125 inputFlags |= Helper::InputFlags::kSnapVerticesToPixelCenters;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500126 }
Chris Daltonbaa1b352019-04-03 12:03:00 -0600127 return Helper::FactoryHelper<NonAAStrokeRectOp>(context, std::move(paint), inputFlags,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500128 viewMatrix, rect,
129 stroke, aaType);
130 }
131
132 NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Chris Daltonbaa1b352019-04-03 12:03:00 -0600133 Helper::InputFlags inputFlags, const SkMatrix& viewMatrix, const SkRect& rect,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500134 const SkStrokeRec& stroke, GrAAType aaType)
Chris Daltonbaa1b352019-04-03 12:03:00 -0600135 : INHERITED(ClassID()), fHelper(helperArgs, aaType, inputFlags) {
Michael Ludwig72ab3462018-12-10 12:43:36 -0500136 fColor = color;
137 fViewMatrix = viewMatrix;
138 fRect = rect;
139 // Sort the rect for hairlines
140 fRect.sort();
141 fStrokeWidth = stroke.getWidth();
142
143 SkScalar rad = SkScalarHalf(fStrokeWidth);
144 SkRect bounds = rect;
145 bounds.outset(rad, rad);
146
147 // If our caller snaps to pixel centers then we have to round out the bounds
Chris Daltonbaa1b352019-04-03 12:03:00 -0600148 if (inputFlags & Helper::InputFlags::kSnapVerticesToPixelCenters) {
Michael Ludwig72ab3462018-12-10 12:43:36 -0500149 viewMatrix.mapRect(&bounds);
150 // We want to be consistent with how we snap non-aa lines. To match what we do in
151 // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
152 // pixel to force us to pixel centers.
153 bounds.set(SkScalarFloorToScalar(bounds.fLeft),
154 SkScalarFloorToScalar(bounds.fTop),
155 SkScalarFloorToScalar(bounds.fRight),
156 SkScalarFloorToScalar(bounds.fBottom));
157 bounds.offset(0.5f, 0.5f);
158 this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
159 } else {
160 this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
161 }
162 }
163
164 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
165
Chris Dalton6ce447a2019-06-23 18:07:38 -0600166 GrProcessorSet::Analysis finalize(
167 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
168 GrClampType clampType) override {
Brian Osman8fa7ab42019-03-18 10:22:42 -0400169 // This Op uses uniform (not vertex) color, so doesn't need to track wide color.
Chris Dalton6ce447a2019-06-23 18:07:38 -0600170 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -0400171 GrProcessorAnalysisCoverage::kNone, &fColor, nullptr);
Michael Ludwig72ab3462018-12-10 12:43:36 -0500172 }
173
174private:
175 void onPrepareDraws(Target* target) override {
176 sk_sp<GrGeometryProcessor> gp;
177 {
178 using namespace GrDefaultGeoProcFactory;
179 Color color(fColor);
180 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
181 ? LocalCoords::kUsePosition_Type
182 : LocalCoords::kUnused_Type;
183 gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(), color,
184 Coverage::kSolid_Type, localCoordsType,
185 fViewMatrix);
186 }
187
188 size_t kVertexStride = gp->vertexStride();
189 int vertexCount = kVertsPerHairlineRect;
190 if (fStrokeWidth > 0) {
191 vertexCount = kVertsPerStrokeRect;
192 }
193
Brian Salomon12d22642019-01-29 14:38:50 -0500194 sk_sp<const GrBuffer> vertexBuffer;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500195 int firstVertex;
196
197 void* verts =
198 target->makeVertexSpace(kVertexStride, vertexCount, &vertexBuffer, &firstVertex);
199
200 if (!verts) {
201 SkDebugf("Could not allocate vertices\n");
202 return;
203 }
204
205 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
206
207 GrPrimitiveType primType;
208 if (fStrokeWidth > 0) {
209 primType = GrPrimitiveType::kTriangleStrip;
210 init_nonaa_stroke_rect_strip(vertex, fRect, fStrokeWidth);
211 } else {
212 // hairline
213 primType = GrPrimitiveType::kLineStrip;
214 vertex[0].set(fRect.fLeft, fRect.fTop);
215 vertex[1].set(fRect.fRight, fRect.fTop);
216 vertex[2].set(fRect.fRight, fRect.fBottom);
217 vertex[3].set(fRect.fLeft, fRect.fBottom);
218 vertex[4].set(fRect.fLeft, fRect.fTop);
219 }
220
221 GrMesh* mesh = target->allocMesh(primType);
222 mesh->setNonIndexedNonInstanced(vertexCount);
Brian Salomon12d22642019-01-29 14:38:50 -0500223 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700224 target->recordDraw(std::move(gp), mesh);
225 }
226
227 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
228 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
Michael Ludwig72ab3462018-12-10 12:43:36 -0500229 }
230
231 // TODO: override onCombineIfPossible
232
233 Helper fHelper;
234 SkPMColor4f fColor;
235 SkMatrix fViewMatrix;
236 SkRect fRect;
237 SkScalar fStrokeWidth;
238
239 const static int kVertsPerHairlineRect = 5;
240 const static int kVertsPerStrokeRect = 10;
241
242 typedef GrMeshDrawOp INHERITED;
243};
244
245///////////////////////////////////////////////////////////////////////////////////////////////////
246// AA Stroking
247///////////////////////////////////////////////////////////////////////////////////////////////////
248
249GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
250GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
251
252static void compute_aa_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
253 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
254 SkScalar strokeWidth, bool miterStroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700255 SkRect devRect;
256 viewMatrix.mapRect(&devRect, rect);
257
258 SkVector devStrokeSize;
259 if (strokeWidth > 0) {
260 devStrokeSize.set(strokeWidth, strokeWidth);
261 viewMatrix.mapVectors(&devStrokeSize, 1);
262 devStrokeSize.setAbs(devStrokeSize);
263 } else {
264 devStrokeSize.set(SK_Scalar1, SK_Scalar1);
265 }
266
267 const SkScalar dx = devStrokeSize.fX;
268 const SkScalar dy = devStrokeSize.fY;
Mike Reed8be952a2017-02-13 20:44:33 -0500269 const SkScalar rx = SkScalarHalf(dx);
270 const SkScalar ry = SkScalarHalf(dy);
bsalomon8b7a9e12016-07-06 13:06:22 -0700271
272 *devOutside = devRect;
273 *devOutsideAssist = devRect;
274 *devInside = devRect;
275
276 devOutside->outset(rx, ry);
277 devInside->inset(rx, ry);
278
279 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
280 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points
281 // together when we render these rects.
282 SkScalar spare;
283 {
284 SkScalar w = devRect.width() - dx;
285 SkScalar h = devRect.height() - dy;
286 spare = SkTMin(w, h);
287 }
288
289 *isDegenerate = spare <= 0;
290 if (*isDegenerate) {
291 devInside->fLeft = devInside->fRight = devRect.centerX();
292 devInside->fTop = devInside->fBottom = devRect.centerY();
293 }
294
295 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
296 // to draw the outside of the octagon. Because there are 8 vertices on the outer
297 // edge, while vertex number of inner edge is 4, the same as miter-stroke.
298 if (!miterStroke) {
299 devOutside->inset(0, ry);
300 devOutsideAssist->outset(0, ry);
301 }
302}
303
Michael Ludwig72ab3462018-12-10 12:43:36 -0500304static sk_sp<GrGeometryProcessor> create_aa_stroke_rect_gp(const GrShaderCaps* shaderCaps,
305 bool tweakAlphaForCoverage,
306 const SkMatrix& viewMatrix,
Brian Osman2a4c4df2018-12-20 14:06:54 -0500307 bool usesLocalCoords,
308 bool wideColor) {
joshualitt9ff64252015-08-10 09:03:51 -0700309 using namespace GrDefaultGeoProcFactory;
310
Brian Osman2a4c4df2018-12-20 14:06:54 -0500311 Coverage::Type coverageType =
312 tweakAlphaForCoverage ? Coverage::kSolid_Type : Coverage::kAttribute_Type;
Brian Salomon8c852be2017-01-04 10:44:42 -0500313 LocalCoords::Type localCoordsType =
Brian Osman2a4c4df2018-12-20 14:06:54 -0500314 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
315 Color::Type colorType =
316 wideColor ? Color::kPremulWideColorAttribute_Type: Color::kPremulGrColorAttribute_Type;
317
318 return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix);
joshualitt9ff64252015-08-10 09:03:51 -0700319}
320
Brian Salomonbaaf4392017-06-15 09:59:23 -0400321class AAStrokeRectOp final : public GrMeshDrawOp {
322private:
323 using Helper = GrSimpleMeshDrawOpHelper;
324
joshualitt3566d442015-09-18 07:12:55 -0700325public:
Brian Salomon25a88092016-12-01 09:36:50 -0500326 DEFINE_OP_CLASS_ID
joshualitt3566d442015-09-18 07:12:55 -0700327
Robert Phillipsb97da532019-02-12 15:24:12 -0500328 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400329 GrPaint&& paint,
330 const SkMatrix& viewMatrix,
331 const SkRect& devOutside,
332 const SkRect& devInside) {
333 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix,
334 devOutside, devInside);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400335 }
336
Brian Osmancf860852018-10-31 14:04:39 -0400337 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400338 const SkMatrix& viewMatrix, const SkRect& devOutside, const SkRect& devInside)
Brian Salomonbaaf4392017-06-15 09:59:23 -0400339 : INHERITED(ClassID())
340 , fHelper(helperArgs, GrAAType::kCoverage)
341 , fViewMatrix(viewMatrix) {
caryclarkd6562002016-07-27 12:02:07 -0700342 SkASSERT(!devOutside.isEmpty());
343 SkASSERT(!devInside.isEmpty());
joshualitt3566d442015-09-18 07:12:55 -0700344
Brian Salomon8c5bad32016-12-20 14:43:36 -0500345 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
bsalomon88cf17d2016-07-08 06:40:56 -0700346 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon8b7a9e12016-07-06 13:06:22 -0700347 fMiterStroke = true;
348 }
349
Robert Phillipsb97da532019-02-12 15:24:12 -0500350 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400351 GrPaint&& paint,
352 const SkMatrix& viewMatrix,
353 const SkRect& rect,
354 const SkStrokeRec& stroke) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700355 bool isMiter;
Michael Ludwig72ab3462018-12-10 12:43:36 -0500356 if (!allowed_stroke(stroke, GrAA::kYes, &isMiter)) {
bsalomon8b7a9e12016-07-06 13:06:22 -0700357 return nullptr;
358 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400359 return Helper::FactoryHelper<AAStrokeRectOp>(context, std::move(paint), viewMatrix, rect,
360 stroke, isMiter);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400361 }
bsalomon8b7a9e12016-07-06 13:06:22 -0700362
Brian Osmancf860852018-10-31 14:04:39 -0400363 AAStrokeRectOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -0400364 const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
365 bool isMiter)
Brian Salomonbaaf4392017-06-15 09:59:23 -0400366 : INHERITED(ClassID())
367 , fHelper(helperArgs, GrAAType::kCoverage)
368 , fViewMatrix(viewMatrix) {
369 fMiterStroke = isMiter;
370 RectInfo& info = fRects.push_back();
Michael Ludwig72ab3462018-12-10 12:43:36 -0500371 compute_aa_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
372 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
Brian Salomon8c5bad32016-12-20 14:43:36 -0500373 info.fColor = color;
Brian Salomon510dd422017-03-16 12:15:22 -0400374 if (isMiter) {
Brian Salomonbaaf4392017-06-15 09:59:23 -0400375 this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon510dd422017-03-16 12:15:22 -0400376 } else {
377 // The outer polygon of the bevel stroke is an octagon specified by the points of a
378 // pair of overlapping rectangles where one is wide and the other is narrow.
379 SkRect bounds = info.fDevOutside;
380 bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
Brian Salomonbaaf4392017-06-15 09:59:23 -0400381 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon510dd422017-03-16 12:15:22 -0400382 }
joshualitt3566d442015-09-18 07:12:55 -0700383 }
384
385 const char* name() const override { return "AAStrokeRect"; }
386
Chris Dalton1706cbf2019-05-21 19:35:29 -0600387 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -0400388 fHelper.visitProxies(func);
389 }
390
Brian Osman9a390ac2018-11-12 09:47:48 -0500391#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -0500392 SkString dumpInfo() const override {
393 SkString string;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500394 for (const auto& info : fRects) {
Brian Salomon6a639042016-12-14 11:08:17 -0500395 string.appendf(
396 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
397 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
398 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
Brian Osmancf860852018-10-31 14:04:39 -0400399 info.fColor.toBytes_RGBA(), info.fDevOutside.fLeft, info.fDevOutside.fTop,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500400 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
401 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
402 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
403 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500404 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -0400405 string += fHelper.dumpInfo();
406 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -0500407 return string;
408 }
Brian Osman9a390ac2018-11-12 09:47:48 -0500409#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -0500410
Brian Salomonbaaf4392017-06-15 09:59:23 -0400411 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
Brian Salomona0485d92017-06-14 19:08:01 -0400412
Chris Dalton6ce447a2019-06-23 18:07:38 -0600413 GrProcessorSet::Analysis finalize(
414 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
415 GrClampType clampType) override {
Chris Daltonb8fff0d2019-03-05 10:11:58 -0700416 return fHelper.finalizeProcessors(
Chris Dalton6ce447a2019-06-23 18:07:38 -0600417 caps, clip, hasMixedSampledCoverage, clampType,
418 GrProcessorAnalysisCoverage::kSingleChannel, &fRects.back().fColor, &fWideColor);
Brian Salomona0485d92017-06-14 19:08:01 -0400419 }
Brian Salomonbaaf4392017-06-15 09:59:23 -0400420
421private:
Brian Salomon91326c32017-08-09 16:02:19 -0400422 void onPrepareDraws(Target*) override;
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700423 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
joshualittaa37a962015-09-18 13:03:25 -0700424
joshualitt3566d442015-09-18 07:12:55 -0700425 static const int kMiterIndexCnt = 3 * 24;
426 static const int kMiterVertexCnt = 16;
427 static const int kNumMiterRectsInIndexBuffer = 256;
428
429 static const int kBevelIndexCnt = 48 + 36 + 24;
430 static const int kBevelVertexCnt = 24;
431 static const int kNumBevelRectsInIndexBuffer = 256;
432
Brian Salomondbf70722019-02-07 11:31:24 -0500433 static sk_sp<const GrGpuBuffer> GetIndexBuffer(GrResourceProvider*, bool miterStroke);
joshualitt3566d442015-09-18 07:12:55 -0700434
joshualittaa37a962015-09-18 13:03:25 -0700435 const SkMatrix& viewMatrix() const { return fViewMatrix; }
436 bool miterStroke() const { return fMiterStroke; }
joshualitt3566d442015-09-18 07:12:55 -0700437
Brian Salomon7eae3e02018-08-07 14:02:38 +0000438 CombineResult onCombineIfPossible(GrOp* t, const GrCaps&) override;
joshualitt3566d442015-09-18 07:12:55 -0700439
Brian Osmancfec9d52018-11-20 11:39:15 -0500440 void generateAAStrokeRectGeometry(GrVertexWriter& vertices,
Brian Osman2a4c4df2018-12-20 14:06:54 -0500441 const SkPMColor4f& color,
442 bool wideColor,
joshualitt3566d442015-09-18 07:12:55 -0700443 const SkRect& devOutside,
444 const SkRect& devOutsideAssist,
445 const SkRect& devInside,
446 bool miterStroke,
joshualitt11edad92015-09-22 10:32:28 -0700447 bool degenerate,
joshualitt3566d442015-09-18 07:12:55 -0700448 bool tweakAlphaForCoverage) const;
449
bsalomon8b7a9e12016-07-06 13:06:22 -0700450 // TODO support AA rotated stroke rects by copying around view matrices
Brian Salomon8c5bad32016-12-20 14:43:36 -0500451 struct RectInfo {
Brian Osmancf860852018-10-31 14:04:39 -0400452 SkPMColor4f fColor;
bsalomon8b7a9e12016-07-06 13:06:22 -0700453 SkRect fDevOutside;
454 SkRect fDevOutsideAssist;
455 SkRect fDevInside;
456 bool fDegenerate;
457 };
458
Brian Salomonbaaf4392017-06-15 09:59:23 -0400459 Helper fHelper;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500460 SkSTArray<1, RectInfo, true> fRects;
joshualittaa37a962015-09-18 13:03:25 -0700461 SkMatrix fViewMatrix;
462 bool fMiterStroke;
Brian Osman2a4c4df2018-12-20 14:06:54 -0500463 bool fWideColor;
joshualitt3566d442015-09-18 07:12:55 -0700464
Brian Salomonbaaf4392017-06-15 09:59:23 -0400465 typedef GrMeshDrawOp INHERITED;
joshualitt3566d442015-09-18 07:12:55 -0700466};
467
Brian Salomon91326c32017-08-09 16:02:19 -0400468void AAStrokeRectOp::onPrepareDraws(Target* target) {
Michael Ludwig72ab3462018-12-10 12:43:36 -0500469 sk_sp<GrGeometryProcessor> gp(create_aa_stroke_rect_gp(target->caps().shaderCaps(),
Brian Osman605c6d52019-03-15 12:10:35 -0400470 fHelper.compatibleWithCoverageAsAlpha(),
Michael Ludwig72ab3462018-12-10 12:43:36 -0500471 this->viewMatrix(),
Brian Osman2a4c4df2018-12-20 14:06:54 -0500472 fHelper.usesLocalCoords(),
473 fWideColor));
joshualitt9ff64252015-08-10 09:03:51 -0700474 if (!gp) {
475 SkDebugf("Couldn't create GrGeometryProcessor\n");
476 return;
477 }
478
joshualitt9ff64252015-08-10 09:03:51 -0700479 int innerVertexNum = 4;
480 int outerVertexNum = this->miterStroke() ? 4 : 8;
481 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
482 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
Brian Salomon8c5bad32016-12-20 14:43:36 -0500483 int instanceCount = fRects.count();
joshualitt9ff64252015-08-10 09:03:51 -0700484
Brian Salomondbf70722019-02-07 11:31:24 -0500485 sk_sp<const GrGpuBuffer> indexBuffer =
Brian Salomon7eae3e02018-08-07 14:02:38 +0000486 GetIndexBuffer(target->resourceProvider(), this->miterStroke());
Brian Salomon12d22642019-01-29 14:38:50 -0500487 if (!indexBuffer) {
488 SkDebugf("Could not allocate indices\n");
489 return;
490 }
491 PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
492 std::move(indexBuffer), verticesPerInstance, indicesPerInstance,
493 instanceCount);
Brian Osmancfec9d52018-11-20 11:39:15 -0500494 GrVertexWriter vertices{ helper.vertices() };
Brian Salomon12d22642019-01-29 14:38:50 -0500495 if (!vertices.fPtr) {
Brian Salomon6a639042016-12-14 11:08:17 -0500496 SkDebugf("Could not allocate vertices\n");
497 return;
498 }
joshualitt9ff64252015-08-10 09:03:51 -0700499
500 for (int i = 0; i < instanceCount; i++) {
Brian Salomon8c5bad32016-12-20 14:43:36 -0500501 const RectInfo& info = fRects[i];
joshualitt9ff64252015-08-10 09:03:51 -0700502 this->generateAAStrokeRectGeometry(vertices,
Brian Osman2a4c4df2018-12-20 14:06:54 -0500503 info.fColor,
504 fWideColor,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500505 info.fDevOutside,
506 info.fDevOutsideAssist,
507 info.fDevInside,
joshualittaa37a962015-09-18 13:03:25 -0700508 fMiterStroke,
Brian Salomon8c5bad32016-12-20 14:43:36 -0500509 info.fDegenerate,
Brian Osman605c6d52019-03-15 12:10:35 -0400510 fHelper.compatibleWithCoverageAsAlpha());
joshualitt9ff64252015-08-10 09:03:51 -0700511 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -0700512 helper.recordDraw(target, std::move(gp));
513}
514
515void AAStrokeRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
516 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt9ff64252015-08-10 09:03:51 -0700517}
518
Brian Salomondbf70722019-02-07 11:31:24 -0500519sk_sp<const GrGpuBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
520 bool miterStroke) {
joshualitt9ff64252015-08-10 09:03:51 -0700521 if (miterStroke) {
Brian Salomon6a639042016-12-14 11:08:17 -0500522 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700523 static const uint16_t gMiterIndices[] = {
524 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
525 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
526 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
527 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
528
529 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
530 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
531 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
532 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
533
534 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
535 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
536 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
537 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
538 };
Brian Salomon6a639042016-12-14 11:08:17 -0500539 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700540 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
541 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
Chris Daltonff926502017-05-03 14:36:54 -0400542 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500543 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
544 gMiterIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700545 } else {
546 /**
547 * As in miter-stroke, index = a + b, and a is the current index, b is the shift
548 * from the first index. The index layout:
549 * outer AA line: 0~3, 4~7
550 * outer edge: 8~11, 12~15
551 * inner edge: 16~19
552 * inner AA line: 20~23
553 * Following comes a bevel-stroke rect and its indices:
554 *
555 * 4 7
556 * *********************************
557 * * ______________________________ *
558 * * / 12 15 \ *
559 * * / \ *
560 * 0 * |8 16_____________________19 11 | * 3
561 * * | | | | *
562 * * | | **************** | | *
563 * * | | * 20 23 * | | *
564 * * | | * * | | *
565 * * | | * 21 22 * | | *
566 * * | | **************** | | *
567 * * | |____________________| | *
568 * 1 * |9 17 18 10| * 2
569 * * \ / *
570 * * \13 __________________________14/ *
571 * * *
572 * **********************************
573 * 5 6
574 */
Brian Salomon6a639042016-12-14 11:08:17 -0500575 // clang-format off
joshualitt9ff64252015-08-10 09:03:51 -0700576 static const uint16_t gBevelIndices[] = {
577 // Draw outer AA, from outer AA line to outer edge, shift is 0.
578 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
579 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
580 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
581 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
582 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
583 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
584 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
585 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
586
587 // Draw the stroke, from outer edge to inner edge, shift is 8.
588 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
589 1 + 8, 5 + 8, 9 + 8,
590 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
591 6 + 8, 2 + 8, 10 + 8,
592 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
593 3 + 8, 7 + 8, 11 + 8,
594 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
595 4 + 8, 0 + 8, 8 + 8,
596
597 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
598 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
599 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
600 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
601 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
602 };
Brian Salomon6a639042016-12-14 11:08:17 -0500603 // clang-format on
joshualitt9ff64252015-08-10 09:03:51 -0700604 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
605
606 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
Chris Daltonff926502017-05-03 14:36:54 -0400607 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon6a639042016-12-14 11:08:17 -0500608 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
609 gBevelIndexBufferKey);
joshualitt9ff64252015-08-10 09:03:51 -0700610 }
611}
612
Brian Salomon7eae3e02018-08-07 14:02:38 +0000613GrOp::CombineResult AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
Brian Salomon6a639042016-12-14 11:08:17 -0500614 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700615
Brian Salomonbaaf4392017-06-15 09:59:23 -0400616 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000617 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700618 }
619
Brian Salomon53e4c3c2016-12-21 11:38:53 -0500620 // TODO combine across miterstroke changes
joshualitt9ff64252015-08-10 09:03:51 -0700621 if (this->miterStroke() != that->miterStroke()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000622 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700623 }
624
625 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses
Brian Salomonbaaf4392017-06-15 09:59:23 -0400626 // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
627 if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +0000628 return CombineResult::kCannotCombine;
joshualitt9ff64252015-08-10 09:03:51 -0700629 }
630
Brian Salomon8c5bad32016-12-20 14:43:36 -0500631 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
Brian Osman2a4c4df2018-12-20 14:06:54 -0500632 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +0000633 return CombineResult::kMerged;
joshualitt9ff64252015-08-10 09:03:51 -0700634}
635
joshualitt11edad92015-09-22 10:32:28 -0700636static void setup_scale(int* scale, SkScalar inset) {
637 if (inset < SK_ScalarHalf) {
638 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
639 SkASSERT(*scale >= 0 && *scale <= 255);
640 } else {
641 *scale = 0xff;
642 }
643}
644
Brian Osmancfec9d52018-11-20 11:39:15 -0500645void AAStrokeRectOp::generateAAStrokeRectGeometry(GrVertexWriter& vertices,
Brian Osman2a4c4df2018-12-20 14:06:54 -0500646 const SkPMColor4f& color,
647 bool wideColor,
Brian Salomon6a639042016-12-14 11:08:17 -0500648 const SkRect& devOutside,
649 const SkRect& devOutsideAssist,
650 const SkRect& devInside,
651 bool miterStroke,
652 bool degenerate,
653 bool tweakAlphaForCoverage) const {
joshualitt9ff64252015-08-10 09:03:51 -0700654 // We create vertices for four nested rectangles. There are two ramps from 0 to full
655 // coverage, one on the exterior of the stroke and the other on the interior.
joshualitt9ff64252015-08-10 09:03:51 -0700656
joshualitt9ff64252015-08-10 09:03:51 -0700657 // TODO: this only really works if the X & Y margins are the same all around
658 // the rect (or if they are all >= 1.0).
joshualitt11edad92015-09-22 10:32:28 -0700659 SkScalar inset;
660 if (!degenerate) {
661 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
662 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
663 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
664 if (miterStroke) {
665 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
666 } else {
Brian Salomon6a639042016-12-14 11:08:17 -0500667 inset = SK_ScalarHalf *
668 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
joshualitt11edad92015-09-22 10:32:28 -0700669 }
670 SkASSERT(inset >= 0);
joshualitt9ff64252015-08-10 09:03:51 -0700671 } else {
joshualitt11edad92015-09-22 10:32:28 -0700672 // TODO use real devRect here
673 inset = SkMinScalar(devOutside.width(), SK_Scalar1);
Brian Salomon6a639042016-12-14 11:08:17 -0500674 inset = SK_ScalarHalf *
675 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
joshualitt9ff64252015-08-10 09:03:51 -0700676 }
joshualitt9ff64252015-08-10 09:03:51 -0700677
Brian Osmancfec9d52018-11-20 11:39:15 -0500678 auto inset_fan = [](const SkRect& r, SkScalar dx, SkScalar dy) {
679 return GrVertexWriter::TriFanFromRect(r.makeInset(dx, dy));
680 };
joshualitt9ff64252015-08-10 09:03:51 -0700681
Brian Osmancfec9d52018-11-20 11:39:15 -0500682 auto maybe_coverage = [tweakAlphaForCoverage](float coverage) {
683 return GrVertexWriter::If(!tweakAlphaForCoverage, coverage);
684 };
685
Brian Osman2a4c4df2018-12-20 14:06:54 -0500686 GrVertexColor outerColor(tweakAlphaForCoverage ? SK_PMColor4fTRANSPARENT : color, wideColor);
Brian Osmancfec9d52018-11-20 11:39:15 -0500687
688 // Outermost rect
689 vertices.writeQuad(inset_fan(devOutside, -SK_ScalarHalf, -SK_ScalarHalf),
690 outerColor,
691 maybe_coverage(0.0f));
692
693 if (!miterStroke) {
694 // Second outermost
695 vertices.writeQuad(inset_fan(devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf),
696 outerColor,
697 maybe_coverage(0.0f));
joshualitt9ff64252015-08-10 09:03:51 -0700698 }
699
700 // scale is the coverage for the the inner two rects.
701 int scale;
joshualitt11edad92015-09-22 10:32:28 -0700702 setup_scale(&scale, inset);
joshualitt9ff64252015-08-10 09:03:51 -0700703
704 float innerCoverage = GrNormalizeByteToFloat(scale);
Brian Osman2a4c4df2018-12-20 14:06:54 -0500705 SkPMColor4f scaledColor = color * innerCoverage;
706 GrVertexColor innerColor(tweakAlphaForCoverage ? scaledColor : color, wideColor);
joshualitt9ff64252015-08-10 09:03:51 -0700707
Brian Osmancfec9d52018-11-20 11:39:15 -0500708 // Inner rect
709 vertices.writeQuad(inset_fan(devOutside, inset, inset),
710 innerColor,
711 maybe_coverage(innerCoverage));
712
713 if (!miterStroke) {
714 // Second inner
715 vertices.writeQuad(inset_fan(devOutsideAssist, inset, inset),
716 innerColor,
717 maybe_coverage(innerCoverage));
joshualitt9ff64252015-08-10 09:03:51 -0700718 }
719
joshualitt11edad92015-09-22 10:32:28 -0700720 if (!degenerate) {
Brian Osmancfec9d52018-11-20 11:39:15 -0500721 vertices.writeQuad(inset_fan(devInside, -inset, -inset),
722 innerColor,
723 maybe_coverage(innerCoverage));
joshualitt11edad92015-09-22 10:32:28 -0700724
Brian Osmancfec9d52018-11-20 11:39:15 -0500725 // The innermost rect has 0 coverage...
726 vertices.writeQuad(inset_fan(devInside, SK_ScalarHalf, SK_ScalarHalf),
Brian Osman2a4c4df2018-12-20 14:06:54 -0500727 GrVertexColor(SK_PMColor4fTRANSPARENT, wideColor),
Brian Osmancfec9d52018-11-20 11:39:15 -0500728 maybe_coverage(0.0f));
729 } else {
730 // When the interior rect has become degenerate we smoosh to a single point
731 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
732
733 vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
734 innerColor,
735 maybe_coverage(innerCoverage));
736
737 // ... unless we are degenerate, in which case we must apply the scaled coverage
738 vertices.writeQuad(GrVertexWriter::TriFanFromRect(devInside),
739 innerColor,
740 maybe_coverage(innerCoverage));
joshualitt9ff64252015-08-10 09:03:51 -0700741 }
742}
743
Michael Ludwig72ab3462018-12-10 12:43:36 -0500744} // anonymous namespace
joshualitt3566d442015-09-18 07:12:55 -0700745
Michael Ludwig72ab3462018-12-10 12:43:36 -0500746namespace GrStrokeRectOp {
747
Robert Phillipsb97da532019-02-12 15:24:12 -0500748std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500749 GrPaint&& paint,
750 GrAAType aaType,
751 const SkMatrix& viewMatrix,
752 const SkRect& rect,
753 const SkStrokeRec& stroke) {
754 if (aaType == GrAAType::kCoverage) {
755 // The AA op only supports axis-aligned rectangles
756 if (!viewMatrix.rectStaysRect()) {
757 return nullptr;
758 }
759 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke);
760 } else {
761 return NonAAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, stroke, aaType);
762 }
763}
764
Robert Phillipsb97da532019-02-12 15:24:12 -0500765std::unique_ptr<GrDrawOp> MakeNested(GrRecordingContext* context,
Michael Ludwig72ab3462018-12-10 12:43:36 -0500766 GrPaint&& paint,
767 const SkMatrix& viewMatrix,
768 const SkRect rects[2]) {
Brian Salomonbaaf4392017-06-15 09:59:23 -0400769 SkASSERT(viewMatrix.rectStaysRect());
770 SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
771
772 SkRect devOutside, devInside;
773 viewMatrix.mapRect(&devOutside, rects[0]);
774 viewMatrix.mapRect(&devInside, rects[1]);
775 if (devInside.isEmpty()) {
776 if (devOutside.isEmpty()) {
777 return nullptr;
778 }
Michael Ludwig4a0cf502019-05-30 12:54:09 -0400779 return GrFillRectOp::Make(context, std::move(paint), GrAAType::kCoverage,
780 GrQuadAAFlags::kAll,
Michael Ludwigde4c58c2019-06-04 09:12:59 -0400781 GrQuad::MakeFromRect(rects[0], viewMatrix),
782 GrQuad(rects[0]));
Brian Salomonbaaf4392017-06-15 09:59:23 -0400783 }
784
Robert Phillips7c525e62018-06-12 10:11:12 -0400785 return AAStrokeRectOp::Make(context, std::move(paint), viewMatrix, devOutside, devInside);
joshualittaa37a962015-09-18 13:03:25 -0700786}
787
Michael Ludwig72ab3462018-12-10 12:43:36 -0500788} // namespace GrStrokeRectOp
joshualitt9ff64252015-08-10 09:03:51 -0700789
Hal Canary6f6961e2017-01-31 13:50:44 -0500790#if GR_TEST_UTILS
joshualitt9ff64252015-08-10 09:03:51 -0700791
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500792#include "src/gpu/GrDrawOpTest.h"
joshualitt9ff64252015-08-10 09:03:51 -0700793
Michael Ludwig72ab3462018-12-10 12:43:36 -0500794GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
795 SkMatrix viewMatrix = GrTest::TestMatrix(random);
796 SkRect rect = GrTest::TestRect(random);
797 SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
798 SkPaint strokePaint;
799 strokePaint.setStrokeWidth(strokeWidth);
800 strokePaint.setStyle(SkPaint::kStroke_Style);
801 strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
802 SkStrokeRec strokeRec(strokePaint);
803 GrAAType aaType = GrAAType::kNone;
Chris Dalton6ce447a2019-06-23 18:07:38 -0600804 if (numSamples > 1) {
Michael Ludwig72ab3462018-12-10 12:43:36 -0500805 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
806 }
807 return NonAAStrokeRectOp::Make(context, std::move(paint), viewMatrix, rect, strokeRec, aaType);
808}
809
Brian Salomonbaaf4392017-06-15 09:59:23 -0400810GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
joshualitt9ff64252015-08-10 09:03:51 -0700811 bool miterStroke = random->nextBool();
812
bsalomon40ef4852016-05-02 13:22:13 -0700813 // Create either a empty rect or a non-empty rect.
Brian Salomon6a639042016-12-14 11:08:17 -0500814 SkRect rect =
815 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
bsalomon40ef4852016-05-02 13:22:13 -0700816 SkScalar minDim = SkMinScalar(rect.width(), rect.height());
817 SkScalar strokeWidth = random->nextUScalar1() * minDim;
joshualitt9ff64252015-08-10 09:03:51 -0700818
bsalomon40ef4852016-05-02 13:22:13 -0700819 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
820 rec.setStrokeStyle(strokeWidth);
821 rec.setStrokeParams(SkPaint::kButt_Cap,
Brian Salomon6a639042016-12-14 11:08:17 -0500822 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
bsalomon40ef4852016-05-02 13:22:13 -0700823 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
Michael Ludwig72ab3462018-12-10 12:43:36 -0500824 return AAStrokeRectOp::Make(context, std::move(paint), matrix, rect, rec);
joshualitt9ff64252015-08-10 09:03:51 -0700825}
826
827#endif