blob: 5bff43b29c48fe2264acd790e39737054c1eb2ba [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 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
Robert Phillips06273bc2021-08-11 15:43:50 -04008#include "src/gpu/ops/GrOvalOpFactory.h"
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -040011#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/core/SkRRectPriv.h"
13#include "src/gpu/GrCaps.h"
14#include "src/gpu/GrDrawOpTest.h"
15#include "src/gpu/GrGeometryProcessor.h"
16#include "src/gpu/GrOpFlushState.h"
17#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050018#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/GrResourceProvider.h"
20#include "src/gpu/GrShaderCaps.h"
21#include "src/gpu/GrStyle.h"
22#include "src/gpu/GrVertexWriter.h"
23#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
25#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/glsl/GrGLSLVarying.h"
27#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
28#include "src/gpu/ops/GrMeshDrawOp.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050029#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
Brian Salomon13b28732021-08-06 15:33:58 -040078 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -040079 b->addBool(fStroke, "stroked" );
80 b->addBool(fInClipPlane.isInitialized(), "clipPlane" );
81 b->addBool(fInIsectPlane.isInitialized(), "isectPlane" );
82 b->addBool(fInUnionPlane.isInitialized(), "unionPlane" );
83 b->addBool(fInRoundCapCenters.isInitialized(), "roundCapCenters");
84 b->addBits(ProgramImpl::kMatrixKeyBits,
85 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
86 "localMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050087 }
88
Brian Salomonf95940b2021-08-09 15:56:24 -040089 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -040090 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050091 }
92
93private:
Greg Daniel2655ede2019-04-10 00:49:28 +000094 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050095 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040096 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040097 , fLocalMatrix(localMatrix)
98 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050099 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500100 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500101 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
102
bsalomon4f3a0ca2016-08-22 13:14:26 -0700103 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400104 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700105 }
106 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400107 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700108 }
109 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400110 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700111 }
Brian Salomon45c92202018-04-10 10:53:58 -0400112 if (roundCaps) {
113 SkASSERT(stroke);
114 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400115 fInRoundCapCenters =
116 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400117 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500118 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000119 }
120
Brian Salomonbab2d112021-08-11 09:59:56 -0400121 class Impl : public ProgramImpl {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400123 void setData(const GrGLSLProgramDataManager& pdman,
124 const GrShaderCaps& shaderCaps,
125 const GrGeometryProcessor& geomProc) override {
126 SetTransform(pdman,
127 shaderCaps,
128 fLocalMatrixUniform,
129 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
130 &fLocalMatrix);
131 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000132
Brian Salomonbab2d112021-08-11 09:59:56 -0400133 private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500134 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400135 const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800136 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800137 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800138 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700139 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800140
joshualittabb52a12015-01-13 15:02:10 -0800141 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800142 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400143 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon48959462021-08-11 13:01:06 -0400144 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge.asShaderVar(), "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400145 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400146 fragBuilder->codeAppend("half3 clipPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400147 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane.asShaderVar(),
148 "clipPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700149 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400150 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400151 fragBuilder->codeAppend("half3 isectPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400152 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane.asShaderVar(),
153 "isectPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700154 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400155 if (cgp.fInUnionPlane.isInitialized()) {
156 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400157 fragBuilder->codeAppend("half3 unionPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400158 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane.asShaderVar(),
159 "unionPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700160 }
Brian Salomon45c92202018-04-10 10:53:58 -0400161 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400162 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400163 fragBuilder->codeAppend("float4 roundCapCenters;");
Brian Salomon48959462021-08-11 13:01:06 -0400164 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters.asShaderVar(),
165 "roundCapCenters");
Brian Salomon45c92202018-04-10 10:53:58 -0400166 varyingHandler->addVarying("capRadius", &capRadius,
167 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
168 // This is the cap radius in normalized space where the outer radius is 1 and
169 // circledEdge.w is the normalized inner radius.
170 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500171 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400172 }
joshualittabb52a12015-01-13 15:02:10 -0800173
joshualittb8c241a2015-05-19 08:23:30 -0700174 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500175 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400176 varyingHandler->addPassThroughAttribute(cgp.fInColor.asShaderVar(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800177
joshualittabb52a12015-01-13 15:02:10 -0800178 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400179 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
180 WriteLocalCoord(vertBuilder,
181 uniformHandler,
182 *args.fShaderCaps,
183 gpArgs,
184 cgp.fInPosition.asShaderVar(),
185 cgp.fLocalMatrix,
186 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800187
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400188 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500189 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000190 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800191 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500192 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000194 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800195 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000197
Brian Salomon92be2f72018-06-19 14:33:47 -0400198 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500199 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000200 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
201 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400202 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500203 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000204 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
205 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700206 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400207 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500208 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000209 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
210 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700211 }
212 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400213 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400214 // We compute coverage of the round caps as circles at the butt caps produced
215 // by the clip planes. The inverse of the clip planes is applied so that there
216 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400217 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500218 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
219 " roundCapCenters.xy)));"
220 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
221 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400222 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400223 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
224 capRadius.fsIn(), capRadius.fsIn());
225 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700226 }
John Stiles4d7ac492021-03-09 20:16:43 -0500227 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 }
229
Michael Ludwig553db622020-06-19 10:47:30 -0400230 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
231 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232 };
233
Brian Salomon289e3d82016-12-14 15:52:56 -0500234 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400235
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500236 Attribute fInPosition;
237 Attribute fInColor;
238 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400239 // Optional attributes.
240 Attribute fInClipPlane;
241 Attribute fInIsectPlane;
242 Attribute fInUnionPlane;
243 Attribute fInRoundCapCenters;
244
Brian Salomon289e3d82016-12-14 15:52:56 -0500245 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400246 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247
John Stiles7571f9e2020-09-02 22:42:33 -0400248 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249};
250
bsalomoncdaa97b2016-03-08 08:30:14 -0800251GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252
Hal Canary6f6961e2017-01-31 13:50:44 -0500253#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500254GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400255 bool stroke = d->fRandom->nextBool();
256 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500257 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400258 bool clipPlane = d->fRandom->nextBool();
259 bool isectPlane = d->fRandom->nextBool();
260 bool unionPlane = d->fRandom->nextBool();
261 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500262 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
263 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264}
Hal Canary6f6961e2017-01-31 13:50:44 -0500265#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400267class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
268public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
270 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600271 return arena->make([&](void* ptr) {
272 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
273 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274 }
275
276 ~ButtCapDashedCircleGeometryProcessor() override {}
277
278 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
279
Brian Salomon13b28732021-08-06 15:33:58 -0400280 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400281 b->addBits(ProgramImpl::kMatrixKeyBits,
282 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
283 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400284 }
285
Brian Salomonf95940b2021-08-09 15:56:24 -0400286 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400287 return std::make_unique<Impl>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400288 }
289
290private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500291 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
292 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
293 , fLocalMatrix(localMatrix) {
294 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
295 fInColor = MakeColorAttribute("inColor", wideColor);
296 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
297 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
298 this->setVertexAttributes(&fInPosition, 4);
299 }
300
Brian Salomonbab2d112021-08-11 09:59:56 -0400301 class Impl : public ProgramImpl {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400302 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400303 void setData(const GrGLSLProgramDataManager& pdman,
304 const GrShaderCaps& shaderCaps,
305 const GrGeometryProcessor& geomProc) override {
306 SetTransform(pdman,
307 shaderCaps,
308 fLocalMatrixUniform,
309 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
310 &fLocalMatrix);
311 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400312
Brian Salomonbab2d112021-08-11 09:59:56 -0400313 private:
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
315 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400316 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400317 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
318 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
319 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
320 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
321
322 // emit attributes
323 varyingHandler->emitAttributes(bcscgp);
324 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon48959462021-08-11 13:01:06 -0400325 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge.asShaderVar(),
326 "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400327
328 fragBuilder->codeAppend("float4 dashParams;");
329 varyingHandler->addPassThroughAttribute(
Brian Salomon48959462021-08-11 13:01:06 -0400330 bcscgp.fInDashParams.asShaderVar(),
331 "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
333 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
334 varyingHandler->addVarying("wrapDashes", &wrapDashes,
335 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
336 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
337 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
338 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500339 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400340 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
341 // x = length of on interval, y = length of on + off.
342 // There are two other parameters in dashParams.zw:
343 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
344 // Each interval has a "corresponding" dash which may be shifted partially or
345 // fully out of its interval by the phase. So there may be up to two "visual"
346 // dashes in an interval.
347 // When computing coverage in an interval we look at three dashes. These are the
348 // "corresponding" dashes from the current, previous, and next intervals. Any of these
349 // may be phase shifted into our interval or even when phase=0 they may be within half a
350 // pixel distance of a pixel center in the interval.
351 // When in the first interval we need to check the dash from the last interval. And
352 // similarly when in the last interval we need to check the dash from the first
353 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
354 // We compute the dash begin/end angles in the vertex shader and apply them in the
355 // fragment shader when we detect we're in the first/last interval.
356 vertBuilder->codeAppend(R"(
357 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
358 // to the fragment shader as a varying.
359 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500360 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400361 // We can happen to be perfectly divisible.
362 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500363 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400364 }
365 // Let 'l' be the last interval before reaching 2 pi.
366 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
367 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
368 // interval.
369 half offset = 0;
370 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500371 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400372 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500373 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400374 }
375 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
376 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
377 // min.
378 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
379
380 // Based on the phase determine whether the -1st, 0th, or 1st interval's
381 // "corresponding" dash appears in the 0th interval and is closest to l.
382 offset = 0;
383 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500384 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400385 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500386 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400387 }
388 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
389 wrapDashes.w = wrapDashes.z + dashParams.x;
390 // The start of the dash we're considering may be clipped by the start of the
391 // circle.
392 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
393 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500394 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
396 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
397 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
398
399 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500400 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400401 varyingHandler->addPassThroughAttribute(
Brian Salomon48959462021-08-11 13:01:06 -0400402 bcscgp.fInColor.asShaderVar(),
403 args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400404 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
405
406 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400407 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
408 WriteLocalCoord(vertBuilder,
409 uniformHandler,
410 *args.fShaderCaps,
411 gpArgs,
412 bcscgp.fInPosition.asShaderVar(),
413 bcscgp.fLocalMatrix,
414 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400415
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 GrShaderVar fnArgs[] = {
417 GrShaderVar("angleToEdge", kFloat_GrSLType),
418 GrShaderVar("diameter", kFloat_GrSLType),
419 };
John Stiles6b58a332020-10-26 17:53:06 -0400420 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
421 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400422 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400423 float linearDist;
424 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
425 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400427 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400428 fragBuilder->codeAppend(R"(
429 float d = length(circleEdge.xy) * circleEdge.z;
430
431 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500432 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400433 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500434 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400435 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 edgeAlpha *= innerAlpha;
437
Ethan Nicholase1f55022019-02-05 17:17:40 -0500438 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400439 angleFromStart = mod(angleFromStart, 6.28318530718);
440 float x = mod(angleFromStart, dashParams.y);
441 // Convert the radial distance from center to pixel into a diameter.
442 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500443 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
444 half(dashParams.w));
445 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
446 half(dashParams.y) + half(dashParams.x) -
447 half(dashParams.w));
448 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
449 half(-dashParams.y) + half(dashParams.x) -
450 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400451 half dashAlpha = 0;
452 )");
453 fragBuilder->codeAppendf(R"(
454 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500455 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400456 currDash.y = min(currDash.y, lastIntervalLength);
457 if (nextDash.x >= lastIntervalLength) {
458 // The next dash is outside the 0..2pi range, throw it away
459 nextDash.xy = half2(1000);
460 } else {
461 // Clip the end of the next dash to the end of the circle
462 nextDash.y = min(nextDash.y, lastIntervalLength);
463 }
464 }
465 )", fnName.c_str(), fnName.c_str());
466 fragBuilder->codeAppendf(R"(
467 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500468 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400469 currDash.x = max(currDash.x, 0);
470 if (prevDash.y <= 0) {
471 // The previous dash is outside the 0..2pi range, throw it away
472 prevDash.xy = half2(1000);
473 } else {
474 // Clip the start previous dash to the start of the circle
475 prevDash.x = max(prevDash.x, 0);
476 }
477 }
478 )", fnName.c_str(), fnName.c_str());
479 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500480 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
481 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
482 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483 dashAlpha = min(dashAlpha, 1);
484 edgeAlpha *= dashAlpha;
485 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
486 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500487 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400488 }
489
Michael Ludwig553db622020-06-19 10:47:30 -0400490 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
491 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400492 };
493
494 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500495 Attribute fInPosition;
496 Attribute fInColor;
497 Attribute fInCircleEdge;
498 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400499
500 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
501
John Stiles7571f9e2020-09-02 22:42:33 -0400502 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400503};
504
505#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500506GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500507 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500509 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400510}
511#endif
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/**
516 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000517 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
518 * in both x and y directions.
519 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000520 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000521 */
522
bsalomoncdaa97b2016-03-08 08:30:14 -0800523class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
526 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600527 return arena->make([&](void* ptr) {
528 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
529 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500530 }
531
532 ~EllipseGeometryProcessor() override {}
533
534 const char* name() const override { return "EllipseGeometryProcessor"; }
535
Brian Salomon13b28732021-08-06 15:33:58 -0400536 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400537 b->addBool(fStroke, "stroked");
538 b->addBits(ProgramImpl::kMatrixKeyBits,
539 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
540 "localMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500541 }
542
Brian Salomonf95940b2021-08-09 15:56:24 -0400543 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400544 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500545 }
546
547private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000548 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400549 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500550 : INHERITED(kEllipseGeometryProcessor_ClassID)
551 , fLocalMatrix(localMatrix)
552 , fStroke(stroke)
553 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500554 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500555 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400556 if (useScale) {
557 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
558 } else {
559 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
560 }
561 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500562 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563 }
564
Brian Salomonbab2d112021-08-11 09:59:56 -0400565 class Impl : public ProgramImpl {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400567 void setData(const GrGLSLProgramDataManager& pdman,
568 const GrShaderCaps& shaderCaps,
569 const GrGeometryProcessor& geomProc) override {
570 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
571 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
572 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000573
Brian Salomonbab2d112021-08-11 09:59:56 -0400574 private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500575 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400576 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800577 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800578 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800579 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000580
joshualittabb52a12015-01-13 15:02:10 -0800581 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800582 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800583
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400584 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
585 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800586 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800587 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500588 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000589
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400590 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800591 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500592 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800593
Chris Dalton60283612018-02-14 13:38:14 -0700594 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700595 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500596 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400597 varyingHandler->addPassThroughAttribute(egp.fInColor.asShaderVar(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800598
joshualittabb52a12015-01-13 15:02:10 -0800599 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400600 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
601 WriteLocalCoord(vertBuilder,
602 uniformHandler,
603 *args.fShaderCaps,
604 gpArgs,
605 egp.fInPosition.asShaderVar(),
606 egp.fLocalMatrix,
607 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800608
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400609 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
610 // to compute both the edges because we need two separate test equations for
611 // the single offset.
612 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
613 // the distance by the gradient, non-uniformly scaled by the inverse of the
614 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800615
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400616 // On medium precision devices, we scale the denominator of the distance equation
617 // before taking the inverse square root to minimize the chance that we're dividing
618 // by zero, then we scale the result back.
619
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000620 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400621 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400622 if (egp.fStroke) {
623 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
624 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400625 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
626 if (egp.fUseScale) {
627 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
628 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
629 } else {
630 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
631 }
632 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700633
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000634 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 if (args.fShaderCaps->floatIs32Bits()) {
636 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
637 } else {
638 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
639 }
640 if (egp.fUseScale) {
641 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
642 ellipseOffsets.fsIn());
643 } else {
644 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
645 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000646 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000647
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000648 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800649 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400650 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800651 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400652 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400653 if (egp.fUseScale) {
654 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
655 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
656 } else {
657 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
658 }
659 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
660 if (!args.fShaderCaps->floatIs32Bits()) {
661 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
662 }
663 if (egp.fUseScale) {
664 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
665 ellipseOffsets.fsIn());
666 } else {
667 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
668 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000669 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000670 }
671
John Stiles4d7ac492021-03-09 20:16:43 -0500672 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673 }
674
Brian Salomonf95940b2021-08-09 15:56:24 -0400675 using INHERITED = ProgramImpl;
Michael Ludwig553db622020-06-19 10:47:30 -0400676
677 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
678 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679 };
680
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500681 Attribute fInPosition;
682 Attribute fInColor;
683 Attribute fInEllipseOffset;
684 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400685
joshualitte3ababe2015-05-15 07:56:07 -0700686 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000687 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400688 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400690 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691
John Stiles7571f9e2020-09-02 22:42:33 -0400692 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693};
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
Hal Canary6f6961e2017-01-31 13:50:44 -0500697#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500698GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400699 bool stroke = d->fRandom->nextBool();
700 bool wideColor = d->fRandom->nextBool();
701 bool useScale = d->fRandom->nextBool();
702 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
703 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704}
Hal Canary6f6961e2017-01-31 13:50:44 -0500705#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000706
707///////////////////////////////////////////////////////////////////////////////
708
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000709/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000710 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000711 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
712 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
713 * using differentials.
714 *
715 * The result is device-independent and can be used with any affine matrix.
716 */
717
bsalomoncdaa97b2016-03-08 08:30:14 -0800718enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720class DIEllipseGeometryProcessor : public GrGeometryProcessor {
721public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500722 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
723 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600724 return arena->make([&](void* ptr) {
725 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
726 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500727 }
728
729 ~DIEllipseGeometryProcessor() override {}
730
731 const char* name() const override { return "DIEllipseGeometryProcessor"; }
732
Brian Salomon13b28732021-08-06 15:33:58 -0400733 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400734 b->addBits(2, static_cast<uint32_t>(fStyle), "style");
Jim Van Verth14a9b082021-09-20 14:09:52 +0000735 b->addBits(ProgramImpl::kMatrixKeyBits,
736 ProgramImpl::ComputeMatrixKey(caps, fViewMatrix),
737 "viewMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500738 }
739
Brian Salomonf95940b2021-08-09 15:56:24 -0400740 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400741 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500742 }
743
744private:
Jim Van Verth14a9b082021-09-20 14:09:52 +0000745 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
746 DIEllipseStyle style)
747 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
748 , fViewMatrix(viewMatrix)
749 , fUseScale(useScale)
750 , fStyle(style) {
751 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
752 fInColor = MakeColorAttribute("inColor", wideColor);
753 if (useScale) {
754 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
755 kFloat3_GrSLType};
756 } else {
757 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
758 kFloat2_GrSLType};
759 }
760 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
761 this->setVertexAttributes(&fInPosition, 4);
762 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763
Brian Salomonbab2d112021-08-11 09:59:56 -0400764 class Impl : public ProgramImpl {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000765 public:
Jim Van Verth14a9b082021-09-20 14:09:52 +0000766 void setData(const GrGLSLProgramDataManager& pdman,
767 const GrShaderCaps& shaderCaps,
768 const GrGeometryProcessor& geomProc) override {
769 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
770
771 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
772 }
Brian Salomonbab2d112021-08-11 09:59:56 -0400773
774 private:
joshualitt465283c2015-09-11 08:19:35 -0700775 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400776 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800777 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800778 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
Jim Van Verth14a9b082021-09-20 14:09:52 +0000779 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000780
joshualittabb52a12015-01-13 15:02:10 -0800781 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800782 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800783
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400784 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
785 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800786 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500787 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700788
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400789 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800790 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500791 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800792
Chris Dalton60283612018-02-14 13:38:14 -0700793 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500794 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400795 varyingHandler->addPassThroughAttribute(diegp.fInColor.asShaderVar(),
796 args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800797
Jim Van Verth14a9b082021-09-20 14:09:52 +0000798 // Setup position
799 WriteOutputPosition(vertBuilder,
800 uniformHandler,
801 *args.fShaderCaps,
802 gpArgs,
803 diegp.fInPosition.name(),
804 diegp.fViewMatrix,
805 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400806 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800807
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000808 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
810 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
811 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
812 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500813 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400814 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
815 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500816 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400817 if (diegp.fUseScale) {
818 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
819 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000820
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400821 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000822 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400823 if (args.fShaderCaps->floatIs32Bits()) {
824 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
825 } else {
826 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
827 }
828 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
829 if (diegp.fUseScale) {
830 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
831 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800832 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000833 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000834 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
835 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000836 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000837 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000838 }
839
840 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800841 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800842 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
843 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400844 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
845 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500846 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400847 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
848 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500849 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400850 if (diegp.fUseScale) {
851 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
852 }
853 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
854 if (!args.fShaderCaps->floatIs32Bits()) {
855 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
856 }
857 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
858 if (diegp.fUseScale) {
859 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
860 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000861 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000862 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000863
John Stiles4d7ac492021-03-09 20:16:43 -0500864 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000865 }
Jim Van Verth14a9b082021-09-20 14:09:52 +0000866
867 SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
868 UniformHandle fViewMatrixUniform;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000869 };
870
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500871 Attribute fInPosition;
872 Attribute fInColor;
873 Attribute fInEllipseOffsets0;
874 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400875
Brian Salomon289e3d82016-12-14 15:52:56 -0500876 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400877 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500878 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400880 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000881
John Stiles7571f9e2020-09-02 22:42:33 -0400882 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000883};
884
bsalomoncdaa97b2016-03-08 08:30:14 -0800885GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886
Hal Canary6f6961e2017-01-31 13:50:44 -0500887#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500888GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400889 bool wideColor = d->fRandom->nextBool();
890 bool useScale = d->fRandom->nextBool();
891 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
892 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
893 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000894}
Hal Canary6f6961e2017-01-31 13:50:44 -0500895#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000896
897///////////////////////////////////////////////////////////////////////////////
898
jvanverth6ca48822016-10-07 06:57:32 -0700899// We have two possible cases for geometry for a circle:
900
901// In the case of a normal fill, we draw geometry for the circle as an octagon.
902static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500903 // enter the octagon
904 // clang-format off
905 0, 1, 8, 1, 2, 8,
906 2, 3, 8, 3, 4, 8,
907 4, 5, 8, 5, 6, 8,
908 6, 7, 8, 7, 0, 8
909 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700910};
911
912// For stroked circles, we use two nested octagons.
913static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 // enter the octagon
915 // clang-format off
916 0, 1, 9, 0, 9, 8,
917 1, 2, 10, 1, 10, 9,
918 2, 3, 11, 2, 11, 10,
919 3, 4, 12, 3, 12, 11,
920 4, 5, 13, 4, 13, 12,
921 5, 6, 14, 5, 14, 13,
922 6, 7, 15, 6, 15, 14,
923 7, 0, 8, 7, 8, 15,
924 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700925};
926
Brian Osman9d958b52018-11-13 12:46:56 -0500927// Normalized geometry for octagons that circumscribe and lie on a circle:
928
929static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
930static constexpr SkPoint kOctagonOuter[] = {
931 SkPoint::Make(-kOctOffset, -1),
932 SkPoint::Make( kOctOffset, -1),
933 SkPoint::Make( 1, -kOctOffset),
934 SkPoint::Make( 1, kOctOffset),
935 SkPoint::Make( kOctOffset, 1),
936 SkPoint::Make(-kOctOffset, 1),
937 SkPoint::Make(-1, kOctOffset),
938 SkPoint::Make(-1, -kOctOffset),
939};
940
941// cosine and sine of pi/8
942static constexpr SkScalar kCosPi8 = 0.923579533f;
943static constexpr SkScalar kSinPi8 = 0.382683432f;
944static constexpr SkPoint kOctagonInner[] = {
945 SkPoint::Make(-kSinPi8, -kCosPi8),
946 SkPoint::Make( kSinPi8, -kCosPi8),
947 SkPoint::Make( kCosPi8, -kSinPi8),
948 SkPoint::Make( kCosPi8, kSinPi8),
949 SkPoint::Make( kSinPi8, kCosPi8),
950 SkPoint::Make(-kSinPi8, kCosPi8),
951 SkPoint::Make(-kCosPi8, kSinPi8),
952 SkPoint::Make(-kCosPi8, -kSinPi8),
953};
Brian Salomon289e3d82016-12-14 15:52:56 -0500954
jvanverth6ca48822016-10-07 06:57:32 -0700955static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
956static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
957static const int kVertsPerStrokeCircle = 16;
958static const int kVertsPerFillCircle = 9;
959
960static int circle_type_to_vert_count(bool stroked) {
961 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
962}
963
964static int circle_type_to_index_count(bool stroked) {
965 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
966}
967
968static const uint16_t* circle_type_to_indices(bool stroked) {
969 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
970}
971
972///////////////////////////////////////////////////////////////////////////////
973
Brian Salomon05441c42017-05-15 16:45:49 -0400974class CircleOp final : public GrMeshDrawOp {
975private:
976 using Helper = GrSimpleMeshDrawOpHelper;
977
joshualitt76e7fb62015-02-11 08:52:27 -0800978public:
Brian Salomon25a88092016-12-01 09:36:50 -0500979 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700980
bsalomon4f3a0ca2016-08-22 13:14:26 -0700981 /** Optional extra params to render a partial arc rather than a full circle. */
982 struct ArcParams {
983 SkScalar fStartAngleRadians;
984 SkScalar fSweepAngleRadians;
985 bool fUseCenter;
986 };
Brian Salomon05441c42017-05-15 16:45:49 -0400987
Herb Derbyc76d4092020-10-07 16:46:15 -0400988 static GrOp::Owner Make(GrRecordingContext* context,
989 GrPaint&& paint,
990 const SkMatrix& viewMatrix,
991 SkPoint center,
992 SkScalar radius,
993 const GrStyle& style,
994 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700995 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700996 if (style.hasPathEffect()) {
997 return nullptr;
998 }
Brian Salomon05441c42017-05-15 16:45:49 -0400999 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001000 SkStrokeRec::Style recStyle = stroke.getStyle();
1001 if (arcParams) {
1002 // Arc support depends on the style.
1003 switch (recStyle) {
1004 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001005 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001006 return nullptr;
1007 case SkStrokeRec::kFill_Style:
1008 // This supports all fills.
1009 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001010 case SkStrokeRec::kStroke_Style:
1011 // Strokes that don't use the center point are supported with butt and round
1012 // caps.
1013 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1014 return nullptr;
1015 }
1016 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001017 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001018 // Hairline only supports butt cap. Round caps could be emulated by slightly
1019 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1021 return nullptr;
1022 }
1023 break;
1024 }
1025 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001026 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1027 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001028 }
1029
Herb Derbyc76d4092020-10-07 16:46:15 -04001030 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001031 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1032 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001033 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001034 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001035 const SkStrokeRec& stroke = style.strokeRec();
1036 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001037
Brian Salomon45c92202018-04-10 10:53:58 -04001038 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001039
bsalomon4b4a7cc2016-07-08 04:42:54 -07001040 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001042 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001043
Brian Salomon289e3d82016-12-14 15:52:56 -05001044 bool isStrokeOnly =
1045 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001046 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001047
jvanverth6ca48822016-10-07 06:57:32 -07001048 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001049 SkScalar outerRadius = radius;
1050 SkScalar halfWidth = 0;
1051 if (hasStroke) {
1052 if (SkScalarNearlyZero(strokeWidth)) {
1053 halfWidth = SK_ScalarHalf;
1054 } else {
1055 halfWidth = SkScalarHalf(strokeWidth);
1056 }
1057
1058 outerRadius += halfWidth;
1059 if (isStrokeOnly) {
1060 innerRadius = radius - halfWidth;
1061 }
1062 }
1063
1064 // The radii are outset for two reasons. First, it allows the shader to simply perform
1065 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1066 // Second, the outer radius is used to compute the verts of the bounding box that is
1067 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001068 outerRadius += SK_ScalarHalf;
1069 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001070 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001071 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001072
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 // This makes every point fully inside the intersection plane.
1074 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1075 // This makes every point fully outside the union plane.
1076 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001077 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1079 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001080 if (arcParams) {
1081 // The shader operates in a space where the circle is translated to be centered at the
1082 // origin. Here we compute points on the unit circle at the starting and ending angles.
1083 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001084 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1085 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001086 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001087 stopPoint.fY = SkScalarSin(endAngle);
1088 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001089
1090 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1091 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1092 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1093 startPoint.normalize();
1094 stopPoint.normalize();
1095
Brian Salomon3517aa72019-12-11 08:16:22 -05001096 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1097 // should orient the clip planes for arcs.
1098 SkASSERT(viewMatrix.isSimilarity());
1099 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1100 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1101 if (upperLeftDet < 0) {
1102 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001103 }
1104
Brian Salomon45c92202018-04-10 10:53:58 -04001105 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1106 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1107 SkPoint roundCaps[2];
1108 if (fRoundCaps) {
1109 // Compute the cap center points in the normalized space.
1110 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1111 roundCaps[0] = startPoint * midRadius;
1112 roundCaps[1] = stopPoint * midRadius;
1113 } else {
1114 roundCaps[0] = kUnusedRoundCaps[0];
1115 roundCaps[1] = kUnusedRoundCaps[1];
1116 }
1117
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001119 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1120 // center of the butts.
1121 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001123 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001124 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001125 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1126 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1127 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 if (useCenter) {
1129 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1130 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001131 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1132 if (arcParams->fSweepAngleRadians < 0) {
1133 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001134 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001135 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001136 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001137 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001138 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001139 color,
1140 innerRadius,
1141 outerRadius,
1142 {norm0.fX, norm0.fY, 0.5f},
1143 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1144 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001145 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001146 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001147 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001148 fClipPlaneIsect = false;
1149 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001150 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001151 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001152 color,
1153 innerRadius,
1154 outerRadius,
1155 {norm0.fX, norm0.fY, 0.5f},
1156 {norm1.fX, norm1.fY, 0.5f},
1157 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001158 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001159 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001160 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001161 fClipPlaneIsect = true;
1162 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001163 }
1164 } else {
1165 // We clip to a secant of the original circle.
1166 startPoint.scale(radius);
1167 stopPoint.scale(radius);
1168 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1169 norm.normalize();
1170 if (arcParams->fSweepAngleRadians > 0) {
1171 norm.negate();
1172 }
1173 SkScalar d = -norm.dot(startPoint) + 0.5f;
1174
Brian Salomon05441c42017-05-15 16:45:49 -04001175 fCircles.emplace_back(
1176 Circle{color,
1177 innerRadius,
1178 outerRadius,
1179 {norm.fX, norm.fY, d},
1180 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1181 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001182 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001183 devBounds,
1184 stroked});
1185 fClipPlane = true;
1186 fClipPlaneIsect = false;
1187 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001188 }
1189 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001190 fCircles.emplace_back(
1191 Circle{color,
1192 innerRadius,
1193 outerRadius,
1194 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1195 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1196 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001197 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001198 devBounds,
1199 stroked});
1200 fClipPlane = false;
1201 fClipPlaneIsect = false;
1202 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001203 }
bsalomon88cf17d2016-07-08 06:40:56 -07001204 // Use the original radius and stroke radius for the bounds so that it does not include the
1205 // AA bloat.
1206 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001207 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001208 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001209 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001210 fVertCount = circle_type_to_vert_count(stroked);
1211 fIndexCount = circle_type_to_index_count(stroked);
1212 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001213 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001214
Brian Salomon289e3d82016-12-14 15:52:56 -05001215 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001216
Robert Phillips294723d2021-06-17 09:23:58 -04001217 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001218 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001219 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001220 } else {
1221 fHelper.visitProxies(func);
1222 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001223 }
1224
Chris Dalton57ab06c2021-04-22 12:57:28 -06001225 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1226 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001227 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001228 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001229 GrProcessorAnalysisCoverage::kSingleChannel, color,
1230 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001231 }
1232
1233 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1234
bsalomone46f9fe2015-08-18 06:05:14 -07001235private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001236 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001237
Robert Phillips4133dc42020-03-11 15:55:55 -04001238 void onCreateProgramInfo(const GrCaps* caps,
1239 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001240 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001241 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001243 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001244 GrXferBarrierFlags renderPassXferBarriers,
1245 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001246 SkASSERT(!usesMSAASurface);
1247
bsalomoncdaa97b2016-03-08 08:30:14 -08001248 SkMatrix localMatrix;
1249 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001250 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001251 }
1252
Robert Phillips4490d922020-03-03 14:50:59 -05001253 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001254 fClipPlaneIsect, fClipPlaneUnion,
1255 fRoundCaps, fWideColor,
1256 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001257
Chris Daltoneb0195e2021-08-18 21:39:02 -06001258 fProgramInfo = fHelper.createProgramInfo(caps,
1259 arena,
1260 writeView,
Chris Dalton2a26c502021-08-26 10:05:11 -06001261 usesMSAASurface,
Chris Daltoneb0195e2021-08-18 21:39:02 -06001262 std::move(appliedClip),
1263 dstProxyView,
1264 gp,
1265 GrPrimitiveType::kTriangles,
1266 renderPassXferBarriers,
1267 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001268 }
1269
Robert Phillips71143952021-06-17 14:55:07 -04001270 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001271 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001272 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001273 if (!fProgramInfo) {
1274 return;
1275 }
1276 }
1277
Brian Salomon12d22642019-01-29 14:38:50 -05001278 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001279 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001280 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001281 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001282 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001283 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001284 return;
1285 }
1286
Brian Salomon12d22642019-01-29 14:38:50 -05001287 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001288 int firstIndex = 0;
1289 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1290 if (!indices) {
1291 SkDebugf("Could not allocate indices\n");
1292 return;
1293 }
1294
1295 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001296 for (const auto& circle : fCircles) {
1297 SkScalar innerRadius = circle.fInnerRadius;
1298 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001299 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001300 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001301
joshualitt76e7fb62015-02-11 08:52:27 -08001302 // The inner radius in the vertex data must be specified in normalized space.
1303 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001304 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001305
1306 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001307 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001308
Brian Osman9a24fee2018-08-03 09:48:42 -04001309 SkVector geoClipPlane = { 0, 0 };
1310 SkScalar offsetClipDist = SK_Scalar1;
1311 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1312 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1313 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1314 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1315 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1316 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1317 // the AA can extend just past the center of the circle.
1318 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1319 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1320 SkAssertResult(geoClipPlane.normalize());
1321 offsetClipDist = 0.5f / halfWidth;
1322 }
1323
Brian Osman7d8f82b2018-11-08 10:24:09 -05001324 for (int i = 0; i < 8; ++i) {
1325 // This clips the normalized offset to the half-plane we computed above. Then we
1326 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001327 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001328 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001329 vertices.write(center + offset * halfWidth,
1330 color,
1331 offset,
1332 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001333 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001334 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001335 }
1336 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001337 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001338 }
1339 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001340 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001341 }
1342 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001343 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001344 }
Brian Salomon45c92202018-04-10 10:53:58 -04001345 }
jvanverth6ca48822016-10-07 06:57:32 -07001346
Brian Salomon05441c42017-05-15 16:45:49 -04001347 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001348 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001349
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001351 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1352 color,
1353 kOctagonInner[i] * innerRadius,
1354 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001355 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 }
1358 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001359 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001360 }
1361 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001362 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001363 }
1364 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001366 }
Brian Salomon45c92202018-04-10 10:53:58 -04001367 }
jvanverth6ca48822016-10-07 06:57:32 -07001368 } else {
1369 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001370 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001371 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001372 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001373 }
jvanverth6ca48822016-10-07 06:57:32 -07001374 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001375 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001376 }
1377 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001378 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001379 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001380 if (fRoundCaps) {
1381 vertices.write(circle.fRoundCapCenters);
1382 }
jvanverth6ca48822016-10-07 06:57:32 -07001383 }
1384
Brian Salomon05441c42017-05-15 16:45:49 -04001385 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1386 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001387 for (int i = 0; i < primIndexCount; ++i) {
1388 *indices++ = primIndices[i] + currStartVertex;
1389 }
1390
Brian Salomon05441c42017-05-15 16:45:49 -04001391 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001392 }
jvanverth6ca48822016-10-07 06:57:32 -07001393
Robert Phillips4490d922020-03-03 14:50:59 -05001394 fMesh = target->allocMesh();
1395 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001396 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001397 }
1398
1399 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001400 if (!fProgramInfo || !fMesh) {
1401 return;
1402 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001403
Chris Dalton765ed362020-03-16 17:34:44 -06001404 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001405 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001406 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001407 }
1408
Herb Derbye25c3002020-10-27 15:57:27 -04001409 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001410 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001411
1412 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001413 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001414 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001415 }
1416
Brian Salomon05441c42017-05-15 16:45:49 -04001417 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001418 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001419 }
1420
Brian Salomon05441c42017-05-15 16:45:49 -04001421 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001422 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1423 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001424 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001425 }
1426
Brian Salomon289e3d82016-12-14 15:52:56 -05001427 // Because we've set up the ops that don't use the planes with noop values
1428 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001429 fClipPlane |= that->fClipPlane;
1430 fClipPlaneIsect |= that->fClipPlaneIsect;
1431 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001432 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001433 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001434
Brian Salomon05441c42017-05-15 16:45:49 -04001435 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001436 fVertCount += that->fVertCount;
1437 fIndexCount += that->fIndexCount;
1438 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001439 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001440 }
1441
John Stilesaf366522020-08-13 09:57:34 -04001442#if GR_TEST_UTILS
1443 SkString onDumpInfo() const override {
1444 SkString string;
1445 for (int i = 0; i < fCircles.count(); ++i) {
1446 string.appendf(
1447 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1448 "InnerRad: %.2f, OuterRad: %.2f\n",
1449 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1450 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1451 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1452 fCircles[i].fOuterRadius);
1453 }
1454 string += fHelper.dumpInfo();
1455 return string;
1456 }
1457#endif
1458
Brian Salomon05441c42017-05-15 16:45:49 -04001459 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001460 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001461 SkScalar fInnerRadius;
1462 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001463 SkScalar fClipPlane[3];
1464 SkScalar fIsectPlane[3];
1465 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001466 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001467 SkRect fDevBounds;
1468 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001469 };
1470
Brian Salomon289e3d82016-12-14 15:52:56 -05001471 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001472 Helper fHelper;
1473 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001474 int fVertCount;
1475 int fIndexCount;
1476 bool fAllFill;
1477 bool fClipPlane;
1478 bool fClipPlaneIsect;
1479 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001480 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001481 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001482
Chris Daltoneb694b72020-03-16 09:25:50 -06001483 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001484 GrProgramInfo* fProgramInfo = nullptr;
1485
John Stiles7571f9e2020-09-02 22:42:33 -04001486 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001487};
1488
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001489class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1490private:
1491 using Helper = GrSimpleMeshDrawOpHelper;
1492
1493public:
1494 DEFINE_OP_CLASS_ID
1495
Herb Derbyc76d4092020-10-07 16:46:15 -04001496 static GrOp::Owner Make(GrRecordingContext* context,
1497 GrPaint&& paint,
1498 const SkMatrix& viewMatrix,
1499 SkPoint center,
1500 SkScalar radius,
1501 SkScalar strokeWidth,
1502 SkScalar startAngle,
1503 SkScalar onAngle,
1504 SkScalar offAngle,
1505 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001506 SkASSERT(circle_stays_circle(viewMatrix));
1507 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001508 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1509 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001510 onAngle, offAngle, phaseAngle);
1511 }
1512
Herb Derbyc76d4092020-10-07 16:46:15 -04001513 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001514 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1515 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1516 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001517 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001518 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001519 SkASSERT(circle_stays_circle(viewMatrix));
1520 viewMatrix.mapPoints(&center, 1);
1521 radius = viewMatrix.mapRadius(radius);
1522 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1523
1524 // Determine the angle where the circle starts in device space and whether its orientation
1525 // has been reversed.
1526 SkVector start;
1527 bool reflection;
1528 if (!startAngle) {
1529 start = {1, 0};
1530 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001531 start.fY = SkScalarSin(startAngle);
1532 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001533 }
1534 viewMatrix.mapVectors(&start, 1);
1535 startAngle = SkScalarATan2(start.fY, start.fX);
1536 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1537 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1538
1539 auto totalAngle = onAngle + offAngle;
1540 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1541
1542 SkScalar halfWidth = 0;
1543 if (SkScalarNearlyZero(strokeWidth)) {
1544 halfWidth = SK_ScalarHalf;
1545 } else {
1546 halfWidth = SkScalarHalf(strokeWidth);
1547 }
1548
1549 SkScalar outerRadius = radius + halfWidth;
1550 SkScalar innerRadius = radius - halfWidth;
1551
1552 // The radii are outset for two reasons. First, it allows the shader to simply perform
1553 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1554 // Second, the outer radius is used to compute the verts of the bounding box that is
1555 // rendered and the outset ensures the box will cover all partially covered by the circle.
1556 outerRadius += SK_ScalarHalf;
1557 innerRadius -= SK_ScalarHalf;
1558 fViewMatrixIfUsingLocalCoords = viewMatrix;
1559
1560 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1561 center.fX + outerRadius, center.fY + outerRadius);
1562
1563 // We store whether there is a reflection as a negative total angle.
1564 if (reflection) {
1565 totalAngle = -totalAngle;
1566 }
1567 fCircles.push_back(Circle{
1568 color,
1569 outerRadius,
1570 innerRadius,
1571 onAngle,
1572 totalAngle,
1573 startAngle,
1574 phaseAngle,
1575 devBounds
1576 });
1577 // Use the original radius and stroke radius for the bounds so that it does not include the
1578 // AA bloat.
1579 radius += halfWidth;
1580 this->setBounds(
1581 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001582 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001583 fVertCount = circle_type_to_vert_count(true);
1584 fIndexCount = circle_type_to_index_count(true);
1585 }
1586
1587 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1588
Robert Phillips294723d2021-06-17 09:23:58 -04001589 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001590 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001591 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001592 } else {
1593 fHelper.visitProxies(func);
1594 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001595 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001596
Chris Dalton57ab06c2021-04-22 12:57:28 -06001597 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1598 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001599 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001600 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001601 GrProcessorAnalysisCoverage::kSingleChannel, color,
1602 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001603 }
1604
1605 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1606
1607private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001608 GrProgramInfo* programInfo() override { return fProgramInfo; }
1609
Robert Phillips4133dc42020-03-11 15:55:55 -04001610 void onCreateProgramInfo(const GrCaps* caps,
1611 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001612 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001613 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001614 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001615 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001616 GrXferBarrierFlags renderPassXferBarriers,
1617 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001618 SkASSERT(!usesMSAASurface);
1619
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001620 SkMatrix localMatrix;
1621 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001622 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001623 }
1624
1625 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001626 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001627 fWideColor,
1628 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001629
Chris Daltoneb0195e2021-08-18 21:39:02 -06001630 fProgramInfo = fHelper.createProgramInfo(caps,
1631 arena,
1632 writeView,
Chris Dalton2a26c502021-08-26 10:05:11 -06001633 usesMSAASurface,
Chris Daltoneb0195e2021-08-18 21:39:02 -06001634 std::move(appliedClip),
1635 dstProxyView,
1636 gp,
1637 GrPrimitiveType::kTriangles,
1638 renderPassXferBarriers,
1639 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001640 }
1641
Robert Phillips71143952021-06-17 14:55:07 -04001642 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001643 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001644 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001645 if (!fProgramInfo) {
1646 return;
1647 }
1648 }
1649
Brian Salomon12d22642019-01-29 14:38:50 -05001650 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001651 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001652 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001653 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001654 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001655 SkDebugf("Could not allocate vertices\n");
1656 return;
1657 }
1658
Brian Salomon12d22642019-01-29 14:38:50 -05001659 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 int firstIndex = 0;
1661 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1662 if (!indices) {
1663 SkDebugf("Could not allocate indices\n");
1664 return;
1665 }
1666
1667 int currStartVertex = 0;
1668 for (const auto& circle : fCircles) {
1669 // The inner radius in the vertex data must be specified in normalized space so that
1670 // length() can be called with smaller values to avoid precision issues with half
1671 // floats.
1672 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1673 const SkRect& bounds = circle.fDevBounds;
1674 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001675 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1676 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1677 };
1678 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001679 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001680 dashParams.totalAngle = -dashParams.totalAngle;
1681 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001682 }
1683
Brian Osmane3caf2d2018-11-21 13:48:36 -05001684 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001685
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001686 // The bounding geometry for the circle is composed of an outer bounding octagon and
1687 // an inner bounded octagon.
1688
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001689 // Compute the vertices of the outer octagon.
1690 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1691 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001692
1693 auto reflectY = [=](const SkPoint& p) {
1694 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001695 };
Brian Osman9d958b52018-11-13 12:46:56 -05001696
1697 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001698 vertices.write(center + kOctagonOuter[i] * halfWidth,
1699 color,
1700 reflectY(kOctagonOuter[i]),
1701 circle.fOuterRadius,
1702 normInnerRadius,
1703 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001704 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001705
1706 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001707 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001708 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1709 color,
1710 reflectY(kOctagonInner[i]) * normInnerRadius,
1711 circle.fOuterRadius,
1712 normInnerRadius,
1713 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001714 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001715
1716 const uint16_t* primIndices = circle_type_to_indices(true);
1717 const int primIndexCount = circle_type_to_index_count(true);
1718 for (int i = 0; i < primIndexCount; ++i) {
1719 *indices++ = primIndices[i] + currStartVertex;
1720 }
1721
1722 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 }
1724
Robert Phillips4490d922020-03-03 14:50:59 -05001725 fMesh = target->allocMesh();
1726 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001727 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001728 }
1729
1730 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001731 if (!fProgramInfo || !fMesh) {
1732 return;
1733 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001734
Chris Dalton765ed362020-03-16 17:34:44 -06001735 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001736 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001737 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001738 }
1739
Herb Derbye25c3002020-10-27 15:57:27 -04001740 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1742
1743 // can only represent 65535 unique vertices with 16-bit indices
1744 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001745 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001746 }
1747
1748 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001749 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001750 }
1751
1752 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001753 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1754 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001755 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001756 }
1757
1758 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001759 fVertCount += that->fVertCount;
1760 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001761 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001762 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001763 }
1764
John Stilesaf366522020-08-13 09:57:34 -04001765#if GR_TEST_UTILS
1766 SkString onDumpInfo() const override {
1767 SkString string;
1768 for (int i = 0; i < fCircles.count(); ++i) {
1769 string.appendf(
1770 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1771 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1772 "Phase: %.2f\n",
1773 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1774 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1775 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1776 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1777 fCircles[i].fPhaseAngle);
1778 }
1779 string += fHelper.dumpInfo();
1780 return string;
1781 }
1782#endif
1783
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001785 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001786 SkScalar fOuterRadius;
1787 SkScalar fInnerRadius;
1788 SkScalar fOnAngle;
1789 SkScalar fTotalAngle;
1790 SkScalar fStartAngle;
1791 SkScalar fPhaseAngle;
1792 SkRect fDevBounds;
1793 };
1794
1795 SkMatrix fViewMatrixIfUsingLocalCoords;
1796 Helper fHelper;
1797 SkSTArray<1, Circle, true> fCircles;
1798 int fVertCount;
1799 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001800 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001801
Chris Daltoneb694b72020-03-16 09:25:50 -06001802 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001803 GrProgramInfo* fProgramInfo = nullptr;
1804
John Stiles7571f9e2020-09-02 22:42:33 -04001805 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001806};
1807
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001808///////////////////////////////////////////////////////////////////////////////
1809
Brian Salomon05441c42017-05-15 16:45:49 -04001810class EllipseOp : public GrMeshDrawOp {
1811private:
1812 using Helper = GrSimpleMeshDrawOpHelper;
1813
1814 struct DeviceSpaceParams {
1815 SkPoint fCenter;
1816 SkScalar fXRadius;
1817 SkScalar fYRadius;
1818 SkScalar fInnerXRadius;
1819 SkScalar fInnerYRadius;
1820 };
1821
joshualitt76e7fb62015-02-11 08:52:27 -08001822public:
Brian Salomon25a88092016-12-01 09:36:50 -05001823 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001824
Herb Derbyc76d4092020-10-07 16:46:15 -04001825 static GrOp::Owner Make(GrRecordingContext* context,
1826 GrPaint&& paint,
1827 const SkMatrix& viewMatrix,
1828 const SkRect& ellipse,
1829 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001830 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001831 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001832 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1833 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001834 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1835 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001836 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1837 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1838 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1839 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001840
bsalomon4b4a7cc2016-07-08 04:42:54 -07001841 // do (potentially) anisotropic mapping of stroke
1842 SkVector scaledStroke;
1843 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001844 scaledStroke.fX = SkScalarAbs(
1845 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1846 scaledStroke.fY = SkScalarAbs(
1847 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001848
1849 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001850 bool isStrokeOnly =
1851 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1853
Brian Salomon05441c42017-05-15 16:45:49 -04001854 params.fInnerXRadius = 0;
1855 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001856 if (hasStroke) {
1857 if (SkScalarNearlyZero(scaledStroke.length())) {
1858 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1859 } else {
1860 scaledStroke.scale(SK_ScalarHalf);
1861 }
1862
1863 // we only handle thick strokes for near-circular ellipses
1864 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001865 (0.5f * params.fXRadius > params.fYRadius ||
1866 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001867 return nullptr;
1868 }
1869
1870 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001871 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1872 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1873 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1874 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001875 return nullptr;
1876 }
1877
1878 // this is legit only if scale & translation (which should be the case at the moment)
1879 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001880 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1881 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001882 }
1883
Brian Salomon05441c42017-05-15 16:45:49 -04001884 params.fXRadius += scaledStroke.fX;
1885 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001886 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001887
1888 // For large ovals with low precision floats, we fall back to the path renderer.
1889 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1890 // minimum value to avoid divides by zero. With large ovals and low precision this
1891 // leads to blurring at the edge of the oval.
1892 const SkScalar kMaxOvalRadius = 16384;
1893 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1894 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1895 return nullptr;
1896 }
1897
Greg Daniel2655ede2019-04-10 00:49:28 +00001898 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001899 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001900 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001901
Herb Derbyc76d4092020-10-07 16:46:15 -04001902 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001903 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001904 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001905 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001906 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001907 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001908 SkStrokeRec::Style style = stroke.getStyle();
1909 bool isStrokeOnly =
1910 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001911
Brian Salomon05441c42017-05-15 16:45:49 -04001912 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1913 params.fInnerXRadius, params.fInnerYRadius,
1914 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1915 params.fCenter.fY - params.fYRadius,
1916 params.fCenter.fX + params.fXRadius,
1917 params.fCenter.fY + params.fYRadius)});
1918
Greg Daniel5faf4742019-10-01 15:14:44 -04001919 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001920
Brian Salomon05441c42017-05-15 16:45:49 -04001921 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1922 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001923 }
joshualitt76e7fb62015-02-11 08:52:27 -08001924
Brian Salomon289e3d82016-12-14 15:52:56 -05001925 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001926
Robert Phillips294723d2021-06-17 09:23:58 -04001927 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001928 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001929 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001930 } else {
1931 fHelper.visitProxies(func);
1932 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001933 }
1934
Chris Dalton57ab06c2021-04-22 12:57:28 -06001935 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1936 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001937 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1938 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001939 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001940 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001941 GrProcessorAnalysisCoverage::kSingleChannel, color,
1942 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001943 }
1944
1945 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1946
bsalomone46f9fe2015-08-18 06:05:14 -07001947private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001948 GrProgramInfo* programInfo() override { return fProgramInfo; }
1949
Robert Phillips4133dc42020-03-11 15:55:55 -04001950 void onCreateProgramInfo(const GrCaps* caps,
1951 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001952 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001953 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001954 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001955 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001956 GrXferBarrierFlags renderPassXferBarriers,
1957 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001958 SkMatrix localMatrix;
1959 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001960 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001961 }
1962
Robert Phillips4490d922020-03-03 14:50:59 -05001963 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1964 fUseScale, localMatrix);
1965
Chris Daltoneb0195e2021-08-18 21:39:02 -06001966 fProgramInfo = fHelper.createProgramInfo(caps,
1967 arena,
1968 writeView,
Chris Dalton2a26c502021-08-26 10:05:11 -06001969 usesMSAASurface,
Chris Daltoneb0195e2021-08-18 21:39:02 -06001970 std::move(appliedClip),
1971 dstProxyView,
1972 gp,
1973 GrPrimitiveType::kTriangles,
1974 renderPassXferBarriers,
1975 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001976 }
1977
Robert Phillips71143952021-06-17 14:55:07 -04001978 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001979 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001980 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001981 if (!fProgramInfo) {
1982 return;
1983 }
1984 }
1985
Robert Phillips787fd9d2021-03-22 14:48:09 -04001986 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001987 GrVertexWriter verts{helper.vertices()};
1988 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001989 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001990 return;
1991 }
1992
Chris Dalton25da4062021-07-13 14:06:28 -06001993 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
1994 // full sample coverage.
1995 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
1996
Brian Salomon05441c42017-05-15 16:45:49 -04001997 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001998 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001999 SkScalar xRadius = ellipse.fXRadius;
2000 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002001
2002 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002003 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2004 SkScalarInvert(xRadius),
2005 SkScalarInvert(yRadius),
2006 SkScalarInvert(ellipse.fInnerXRadius),
2007 SkScalarInvert(ellipse.fInnerYRadius)
2008 };
Chris Dalton25da4062021-07-13 14:06:28 -06002009 SkScalar xMaxOffset = xRadius + aaBloat;
2010 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07002011
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002012 if (!fStroked) {
2013 // For filled ellipses we map a unit circle in the vertex attributes rather than
2014 // computing an ellipse and modifying that distance, so we normalize to 1
2015 xMaxOffset /= xRadius;
2016 yMaxOffset /= yRadius;
2017 }
2018
joshualitt76e7fb62015-02-11 08:52:27 -08002019 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002020 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2021 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002022 color,
2023 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002024 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002025 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002026 }
Robert Phillips4490d922020-03-03 14:50:59 -05002027 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002028 }
2029
2030 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002031 if (!fProgramInfo || !fMesh) {
2032 return;
2033 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002034
Chris Dalton765ed362020-03-16 17:34:44 -06002035 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002036 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002037 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002038 }
2039
Herb Derbye25c3002020-10-27 15:57:27 -04002040 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002041 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002042
Brian Salomon05441c42017-05-15 16:45:49 -04002043 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002044 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002045 }
2046
bsalomoncdaa97b2016-03-08 08:30:14 -08002047 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002048 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002049 }
2050
Brian Salomon05441c42017-05-15 16:45:49 -04002051 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002052 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2053 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002054 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002055 }
2056
Brian Salomon05441c42017-05-15 16:45:49 -04002057 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002058 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002059 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002060 }
2061
John Stilesaf366522020-08-13 09:57:34 -04002062#if GR_TEST_UTILS
2063 SkString onDumpInfo() const override {
2064 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2065 for (const auto& geo : fEllipses) {
2066 string.appendf(
2067 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2068 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2069 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2070 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2071 geo.fInnerXRadius, geo.fInnerYRadius);
2072 }
2073 string += fHelper.dumpInfo();
2074 return string;
2075 }
2076#endif
2077
Brian Salomon05441c42017-05-15 16:45:49 -04002078 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002079 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002080 SkScalar fXRadius;
2081 SkScalar fYRadius;
2082 SkScalar fInnerXRadius;
2083 SkScalar fInnerYRadius;
2084 SkRect fDevBounds;
2085 };
joshualitt76e7fb62015-02-11 08:52:27 -08002086
Brian Salomon289e3d82016-12-14 15:52:56 -05002087 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002088 Helper fHelper;
2089 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002090 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002091 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002092 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002093
Chris Daltoneb694b72020-03-16 09:25:50 -06002094 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002095 GrProgramInfo* fProgramInfo = nullptr;
2096
John Stiles7571f9e2020-09-02 22:42:33 -04002097 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002098};
2099
joshualitt76e7fb62015-02-11 08:52:27 -08002100/////////////////////////////////////////////////////////////////////////////////////////////////
2101
Brian Salomon05441c42017-05-15 16:45:49 -04002102class DIEllipseOp : public GrMeshDrawOp {
2103private:
2104 using Helper = GrSimpleMeshDrawOpHelper;
2105
2106 struct DeviceSpaceParams {
2107 SkPoint fCenter;
2108 SkScalar fXRadius;
2109 SkScalar fYRadius;
2110 SkScalar fInnerXRadius;
2111 SkScalar fInnerYRadius;
2112 DIEllipseStyle fStyle;
2113 };
2114
joshualitt76e7fb62015-02-11 08:52:27 -08002115public:
Brian Salomon25a88092016-12-01 09:36:50 -05002116 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002117
Herb Derbyc76d4092020-10-07 16:46:15 -04002118 static GrOp::Owner Make(GrRecordingContext* context,
2119 GrPaint&& paint,
2120 const SkMatrix& viewMatrix,
2121 const SkRect& ellipse,
2122 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002123 DeviceSpaceParams params;
2124 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2125 params.fXRadius = SkScalarHalf(ellipse.width());
2126 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002127
bsalomon4b4a7cc2016-07-08 04:42:54 -07002128 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002129 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2130 ? DIEllipseStyle::kStroke
2131 : (SkStrokeRec::kHairline_Style == style)
2132 ? DIEllipseStyle::kHairline
2133 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002134
Brian Salomon05441c42017-05-15 16:45:49 -04002135 params.fInnerXRadius = 0;
2136 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002137 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2138 SkScalar strokeWidth = stroke.getWidth();
2139
2140 if (SkScalarNearlyZero(strokeWidth)) {
2141 strokeWidth = SK_ScalarHalf;
2142 } else {
2143 strokeWidth *= SK_ScalarHalf;
2144 }
2145
2146 // we only handle thick strokes for near-circular ellipses
2147 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002148 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2149 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002150 return nullptr;
2151 }
2152
2153 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002154 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2155 (strokeWidth * strokeWidth) * params.fXRadius) {
2156 return nullptr;
2157 }
2158 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2159 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002160 return nullptr;
2161 }
2162
2163 // set inner radius (if needed)
2164 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002165 params.fInnerXRadius = params.fXRadius - strokeWidth;
2166 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002167 }
2168
Brian Salomon05441c42017-05-15 16:45:49 -04002169 params.fXRadius += strokeWidth;
2170 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002171 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002172
2173 // For large ovals with low precision floats, we fall back to the path renderer.
2174 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2175 // minimum value to avoid divides by zero. With large ovals and low precision this
2176 // leads to blurring at the edge of the oval.
2177 const SkScalar kMaxOvalRadius = 16384;
2178 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2179 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2180 return nullptr;
2181 }
2182
Brian Salomon05441c42017-05-15 16:45:49 -04002183 if (DIEllipseStyle::kStroke == params.fStyle &&
2184 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2185 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002186 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002187 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002188 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002189
Herb Derbyc76d4092020-10-07 16:46:15 -04002190 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002191 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002192 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002193 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002194 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002195 // This expands the outer rect so that after CTM we end up with a half-pixel border
2196 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2197 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2198 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2199 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002200 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2201 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002202
Brian Salomon05441c42017-05-15 16:45:49 -04002203 fEllipses.emplace_back(
2204 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2205 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002206 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2207 params.fCenter.fY - params.fYRadius,
2208 params.fCenter.fX + params.fXRadius,
2209 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002210 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002211 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002212 }
2213
Brian Salomon289e3d82016-12-14 15:52:56 -05002214 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002215
Robert Phillips294723d2021-06-17 09:23:58 -04002216 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002217 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002218 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002219 } else {
2220 fHelper.visitProxies(func);
2221 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002222 }
2223
Chris Dalton57ab06c2021-04-22 12:57:28 -06002224 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2225 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002226 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2227 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002228 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002229 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002230 GrProcessorAnalysisCoverage::kSingleChannel, color,
2231 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002232 }
2233
2234 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2235
bsalomone46f9fe2015-08-18 06:05:14 -07002236private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002237 GrProgramInfo* programInfo() override { return fProgramInfo; }
2238
Robert Phillips4133dc42020-03-11 15:55:55 -04002239 void onCreateProgramInfo(const GrCaps* caps,
2240 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002241 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002242 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002243 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002244 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002245 GrXferBarrierFlags renderPassXferBarriers,
2246 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002247 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2248 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002249 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002250
Chris Dalton2a26c502021-08-26 10:05:11 -06002251 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
2252 std::move(appliedClip), dstProxyView, gp,
2253 GrPrimitiveType::kTriangles,
Chris Daltoneb0195e2021-08-18 21:39:02 -06002254 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002255 }
2256
Robert Phillips71143952021-06-17 14:55:07 -04002257 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002258 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002259 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002260 }
2261
Robert Phillips787fd9d2021-03-22 14:48:09 -04002262 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002263 GrVertexWriter verts{helper.vertices()};
2264 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002265 return;
2266 }
2267
Brian Salomon05441c42017-05-15 16:45:49 -04002268 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002269 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002270 SkScalar xRadius = ellipse.fXRadius;
2271 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002272
Chris Dalton25da4062021-07-13 14:06:28 -06002273 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2274 // full sample coverage.
2275 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2276 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2277 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002278
Chris Dalton25da4062021-07-13 14:06:28 -06002279 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2280 // occurs at x^2 + y^2 == 1.
2281 float outerCoordX = drawBounds.width() / (xRadius * 2);
2282 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002283
Chris Dalton25da4062021-07-13 14:06:28 -06002284 // By default, constructed so that inner coord is (0, 0) for all points
2285 float innerCoordX = 0;
2286 float innerCoordY = 0;
2287
2288 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2289 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002290 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002291 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2292 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002293 }
joshualitt76e7fb62015-02-11 08:52:27 -08002294
Chris Dalton25da4062021-07-13 14:06:28 -06002295 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002296 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002297 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002298 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002299 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002300 }
Robert Phillips4490d922020-03-03 14:50:59 -05002301 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002302 }
2303
2304 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002305 if (!fProgramInfo || !fMesh) {
2306 return;
2307 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002308
Chris Dalton765ed362020-03-16 17:34:44 -06002309 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002310 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002311 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002312 }
halcanary9d524f22016-03-29 09:03:52 -07002313
Herb Derbye25c3002020-10-27 15:57:27 -04002314 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002315 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002316 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002317 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002318 }
2319
bsalomoncdaa97b2016-03-08 08:30:14 -08002320 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002321 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002322 }
2323
joshualittd96a67b2015-05-05 14:09:05 -07002324 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002325 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002326 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002327 }
2328
Brian Salomon05441c42017-05-15 16:45:49 -04002329 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002330 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002331 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002332 }
2333
John Stilesaf366522020-08-13 09:57:34 -04002334#if GR_TEST_UTILS
2335 SkString onDumpInfo() const override {
2336 SkString string;
2337 for (const auto& geo : fEllipses) {
2338 string.appendf(
2339 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2340 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2341 "GeoDY: %.2f\n",
2342 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2343 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2344 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2345 }
2346 string += fHelper.dumpInfo();
2347 return string;
2348 }
2349#endif
2350
Brian Salomon05441c42017-05-15 16:45:49 -04002351 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2352 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002353
Brian Salomon05441c42017-05-15 16:45:49 -04002354 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002355 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002356 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002357 SkScalar fXRadius;
2358 SkScalar fYRadius;
2359 SkScalar fInnerXRadius;
2360 SkScalar fInnerYRadius;
2361 SkScalar fGeoDx;
2362 SkScalar fGeoDy;
2363 DIEllipseStyle fStyle;
2364 SkRect fBounds;
2365 };
2366
Brian Salomon05441c42017-05-15 16:45:49 -04002367 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002368 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002369 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002370 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002371
Chris Daltoneb694b72020-03-16 09:25:50 -06002372 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002373 GrProgramInfo* fProgramInfo = nullptr;
2374
John Stiles7571f9e2020-09-02 22:42:33 -04002375 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002376};
2377
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002378///////////////////////////////////////////////////////////////////////////////
2379
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002380// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002381//
2382// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2383// ____________
2384// |_|________|_|
2385// | | | |
2386// | | | |
2387// | | | |
2388// |_|________|_|
2389// |_|________|_|
2390//
2391// For strokes, we don't draw the center quad.
2392//
2393// For circular roundrects, in the case where the stroke width is greater than twice
2394// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002395// in the center. The shared vertices are duplicated so we can set a different outer radius
2396// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002397// ____________
2398// |_|________|_|
2399// | |\ ____ /| |
2400// | | | | | |
2401// | | |____| | |
2402// |_|/______\|_|
2403// |_|________|_|
2404//
2405// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002406//
2407// For filled rrects that need to provide a distance vector we resuse the overstroke
2408// geometry but make the inner rect degenerate (either a point or a horizontal or
2409// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002410
jvanverth84839f62016-08-29 10:16:40 -07002411static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002412 // clang-format off
2413 // overstroke quads
2414 // we place this at the beginning so that we can skip these indices when rendering normally
2415 16, 17, 19, 16, 19, 18,
2416 19, 17, 23, 19, 23, 21,
2417 21, 23, 22, 21, 22, 20,
2418 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002419
Brian Salomon289e3d82016-12-14 15:52:56 -05002420 // corners
2421 0, 1, 5, 0, 5, 4,
2422 2, 3, 7, 2, 7, 6,
2423 8, 9, 13, 8, 13, 12,
2424 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002425
Brian Salomon289e3d82016-12-14 15:52:56 -05002426 // edges
2427 1, 2, 6, 1, 6, 5,
2428 4, 5, 9, 4, 9, 8,
2429 6, 7, 11, 6, 11, 10,
2430 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002431
Brian Salomon289e3d82016-12-14 15:52:56 -05002432 // center
2433 // we place this at the end so that we can ignore these indices when not rendering as filled
2434 5, 6, 10, 5, 10, 9,
2435 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002436};
Brian Salomon289e3d82016-12-14 15:52:56 -05002437
jvanverth84839f62016-08-29 10:16:40 -07002438// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002439static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002440
jvanverth84839f62016-08-29 10:16:40 -07002441// overstroke count is arraysize minus the center indices
2442static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2443// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002444static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002445// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002446static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2447static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002448static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002449
jvanverthc3d0e422016-08-25 08:12:35 -07002450enum RRectType {
2451 kFill_RRectType,
2452 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002453 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002454};
2455
jvanverth84839f62016-08-29 10:16:40 -07002456static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002457 switch (type) {
2458 case kFill_RRectType:
2459 case kStroke_RRectType:
2460 return kVertsPerStandardRRect;
2461 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002462 return kVertsPerOverstrokeRRect;
2463 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002464 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002465}
2466
2467static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002468 switch (type) {
2469 case kFill_RRectType:
2470 return kIndicesPerFillRRect;
2471 case kStroke_RRectType:
2472 return kIndicesPerStrokeRRect;
2473 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002474 return kIndicesPerOverstrokeRRect;
2475 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002476 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002477}
2478
2479static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002480 switch (type) {
2481 case kFill_RRectType:
2482 case kStroke_RRectType:
2483 return gStandardRRectIndices;
2484 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002485 return gOverstrokeRRectIndices;
2486 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002487 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002488}
2489
joshualitt76e7fb62015-02-11 08:52:27 -08002490///////////////////////////////////////////////////////////////////////////////////////////////////
2491
Robert Phillips79839d42016-10-06 15:03:34 -04002492// For distance computations in the interior of filled rrects we:
2493//
2494// add a interior degenerate (point or line) rect
2495// each vertex of that rect gets -outerRad as its radius
2496// this makes the computation of the distance to the outer edge be negative
2497// negative values are caught and then handled differently in the GP's onEmitCode
2498// each vertex is also given the normalized x & y distance from the interior rect's edge
2499// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2500
Brian Salomon05441c42017-05-15 16:45:49 -04002501class CircularRRectOp : public GrMeshDrawOp {
2502private:
2503 using Helper = GrSimpleMeshDrawOpHelper;
2504
joshualitt76e7fb62015-02-11 08:52:27 -08002505public:
Brian Salomon25a88092016-12-01 09:36:50 -05002506 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002507
bsalomon4b4a7cc2016-07-08 04:42:54 -07002508 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2509 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002510 static GrOp::Owner Make(GrRecordingContext* context,
2511 GrPaint&& paint,
2512 const SkMatrix& viewMatrix,
2513 const SkRect& devRect,
2514 float devRadius,
2515 float devStrokeWidth,
2516 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002517 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002518 devRect, devRadius,
2519 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002520 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002521 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002522 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2523 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002524 : INHERITED(ClassID())
2525 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002526 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002527 SkRect bounds = devRect;
2528 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2529 SkScalar innerRadius = 0.0f;
2530 SkScalar outerRadius = devRadius;
2531 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002532 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002533 if (devStrokeWidth > 0) {
2534 if (SkScalarNearlyZero(devStrokeWidth)) {
2535 halfWidth = SK_ScalarHalf;
2536 } else {
2537 halfWidth = SkScalarHalf(devStrokeWidth);
2538 }
joshualitt76e7fb62015-02-11 08:52:27 -08002539
bsalomon4b4a7cc2016-07-08 04:42:54 -07002540 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002541 // Outset stroke by 1/4 pixel
2542 devStrokeWidth += 0.25f;
2543 // If stroke is greater than width or height, this is still a fill
2544 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002545 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002546 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002547 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002548 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002549 }
2550 outerRadius += halfWidth;
2551 bounds.outset(halfWidth, halfWidth);
2552 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002553
Greg Daniel2655ede2019-04-10 00:49:28 +00002554 // The radii are outset for two reasons. First, it allows the shader to simply perform
2555 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2556 // Second, the outer radius is used to compute the verts of the bounding box that is
2557 // rendered and the outset ensures the box will cover all partially covered by the rrect
2558 // corners.
2559 outerRadius += SK_ScalarHalf;
2560 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002561
Greg Daniel5faf4742019-10-01 15:14:44 -04002562 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002563
Greg Daniel2655ede2019-04-10 00:49:28 +00002564 // Expand the rect for aa to generate correct vertices.
2565 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002566
Brian Salomon05441c42017-05-15 16:45:49 -04002567 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002568 fVertCount = rrect_type_to_vert_count(type);
2569 fIndexCount = rrect_type_to_index_count(type);
2570 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002571 }
2572
Brian Salomon289e3d82016-12-14 15:52:56 -05002573 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002574
Robert Phillips294723d2021-06-17 09:23:58 -04002575 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002576 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002577 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002578 } else {
2579 fHelper.visitProxies(func);
2580 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002581 }
2582
Chris Dalton57ab06c2021-04-22 12:57:28 -06002583 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2584 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002585 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002586 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002587 GrProcessorAnalysisCoverage::kSingleChannel, color,
2588 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002589 }
2590
2591 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2592
Brian Salomon92aee3d2016-12-21 09:20:25 -05002593private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002594 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002595 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002596 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002597 SkASSERT(smInset < bigInset);
2598
2599 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002600 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2601 color,
2602 xOffset, 0.0f,
2603 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002604
2605 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002606 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2607 color,
2608 xOffset, 0.0f,
2609 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002610
Brian Osmana1d4eb92018-12-06 16:33:10 -05002611 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2612 color,
2613 0.0f, 0.0f,
2614 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002615
Brian Osmana1d4eb92018-12-06 16:33:10 -05002616 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2617 color,
2618 0.0f, 0.0f,
2619 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002620
Brian Osmana1d4eb92018-12-06 16:33:10 -05002621 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2622 color,
2623 0.0f, 0.0f,
2624 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002625
Brian Osmana1d4eb92018-12-06 16:33:10 -05002626 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2627 color,
2628 0.0f, 0.0f,
2629 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002630
2631 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002632 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2633 color,
2634 xOffset, 0.0f,
2635 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002636
2637 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002638 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2639 color,
2640 xOffset, 0.0f,
2641 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002642 }
2643
Robert Phillips2669a7b2020-03-12 12:07:19 -04002644 GrProgramInfo* programInfo() override { return fProgramInfo; }
2645
Robert Phillips4133dc42020-03-11 15:55:55 -04002646 void onCreateProgramInfo(const GrCaps* caps,
2647 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002648 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002649 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002650 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002651 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002652 GrXferBarrierFlags renderPassXferBarriers,
2653 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002654 SkASSERT(!usesMSAASurface);
2655
bsalomoncdaa97b2016-03-08 08:30:14 -08002656 // Invert the view matrix as a local matrix (if any other processors require coords).
2657 SkMatrix localMatrix;
2658 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002659 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002660 }
2661
Robert Phillips4490d922020-03-03 14:50:59 -05002662 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002663 false, false, false, false,
2664 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002665
Chris Dalton2a26c502021-08-26 10:05:11 -06002666 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
2667 std::move(appliedClip), dstProxyView, gp,
2668 GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002669 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002670 }
2671
Robert Phillips71143952021-06-17 14:55:07 -04002672 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002673 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002674 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002675 if (!fProgramInfo) {
2676 return;
2677 }
2678 }
2679
Brian Salomon12d22642019-01-29 14:38:50 -05002680 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002681 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002682
Robert Phillips787fd9d2021-03-22 14:48:09 -04002683 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002684 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002685 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002686 SkDebugf("Could not allocate vertices\n");
2687 return;
2688 }
2689
Brian Salomon12d22642019-01-29 14:38:50 -05002690 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002691 int firstIndex = 0;
2692 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2693 if (!indices) {
2694 SkDebugf("Could not allocate indices\n");
2695 return;
2696 }
2697
2698 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002699 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002700 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002701 SkScalar outerRadius = rrect.fOuterRadius;
2702 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002703
Brian Salomon289e3d82016-12-14 15:52:56 -05002704 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2705 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002706
Brian Salomon289e3d82016-12-14 15:52:56 -05002707 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002708 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002709 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002710 SkScalar innerRadius = rrect.fType != kFill_RRectType
2711 ? rrect.fInnerRadius / rrect.fOuterRadius
2712 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002713 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002714 verts.write(bounds.fLeft, yCoords[i],
2715 color,
2716 -1.0f, yOuterRadii[i],
2717 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002718
Brian Osmana1d4eb92018-12-06 16:33:10 -05002719 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2720 color,
2721 0.0f, yOuterRadii[i],
2722 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002723
Brian Osmana1d4eb92018-12-06 16:33:10 -05002724 verts.write(bounds.fRight - outerRadius, yCoords[i],
2725 color,
2726 0.0f, yOuterRadii[i],
2727 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002728
Brian Osmana1d4eb92018-12-06 16:33:10 -05002729 verts.write(bounds.fRight, yCoords[i],
2730 color,
2731 1.0f, yOuterRadii[i],
2732 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002733 }
jvanverthc3d0e422016-08-25 08:12:35 -07002734 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002735 // Effectively this is an additional stroked rrect, with its
2736 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2737 // This will give us correct AA in the center and the correct
2738 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002739 //
jvanvertha4f1af82016-08-29 07:17:47 -07002740 // Also, the outer offset is a constant vector pointing to the right, which
2741 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002742 if (kOverstroke_RRectType == rrect.fType) {
2743 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002744
Brian Salomon05441c42017-05-15 16:45:49 -04002745 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002746 // this is the normalized distance from the outer rectangle of this
2747 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002748 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002749
Brian Osmana1d4eb92018-12-06 16:33:10 -05002750 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002751 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002752 }
jvanverth6a397612016-08-26 08:15:33 -07002753
Brian Salomon05441c42017-05-15 16:45:49 -04002754 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2755 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002756 for (int i = 0; i < primIndexCount; ++i) {
2757 *indices++ = primIndices[i] + currStartVertex;
2758 }
2759
Brian Salomon05441c42017-05-15 16:45:49 -04002760 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002761 }
2762
Robert Phillips4490d922020-03-03 14:50:59 -05002763 fMesh = target->allocMesh();
2764 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002765 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002766 }
2767
2768 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002769 if (!fProgramInfo || !fMesh) {
2770 return;
2771 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002772
Chris Dalton765ed362020-03-16 17:34:44 -06002773 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002774 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002775 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002776 }
2777
Herb Derbye25c3002020-10-27 15:57:27 -04002778 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002779 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002780
2781 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002782 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002783 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002784 }
2785
Brian Salomon05441c42017-05-15 16:45:49 -04002786 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002787 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002788 }
2789
Brian Salomon05441c42017-05-15 16:45:49 -04002790 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002791 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2792 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002793 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002794 }
2795
Brian Salomon05441c42017-05-15 16:45:49 -04002796 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002797 fVertCount += that->fVertCount;
2798 fIndexCount += that->fIndexCount;
2799 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002800 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002801 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002802 }
2803
John Stilesaf366522020-08-13 09:57:34 -04002804#if GR_TEST_UTILS
2805 SkString onDumpInfo() const override {
2806 SkString string;
2807 for (int i = 0; i < fRRects.count(); ++i) {
2808 string.appendf(
2809 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2810 "InnerRad: %.2f, OuterRad: %.2f\n",
2811 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2812 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2813 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2814 fRRects[i].fOuterRadius);
2815 }
2816 string += fHelper.dumpInfo();
2817 return string;
2818 }
2819#endif
2820
Brian Salomon05441c42017-05-15 16:45:49 -04002821 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002822 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002823 SkScalar fInnerRadius;
2824 SkScalar fOuterRadius;
2825 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002826 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002827 };
2828
Brian Salomon289e3d82016-12-14 15:52:56 -05002829 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002830 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002831 int fVertCount;
2832 int fIndexCount;
2833 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002834 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002835 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002836
Chris Daltoneb694b72020-03-16 09:25:50 -06002837 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002838 GrProgramInfo* fProgramInfo = nullptr;
2839
John Stiles7571f9e2020-09-02 22:42:33 -04002840 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002841};
2842
jvanverth84839f62016-08-29 10:16:40 -07002843static const int kNumRRectsInIndexBuffer = 256;
2844
2845GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2846GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002847static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2848 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002849 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2850 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2851 switch (type) {
2852 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002853 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002854 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2855 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002856 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002857 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002858 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2859 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002860 default:
2861 SkASSERT(false);
2862 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002863 }
jvanverth84839f62016-08-29 10:16:40 -07002864}
2865
Brian Salomon05441c42017-05-15 16:45:49 -04002866class EllipticalRRectOp : public GrMeshDrawOp {
2867private:
2868 using Helper = GrSimpleMeshDrawOpHelper;
2869
joshualitt76e7fb62015-02-11 08:52:27 -08002870public:
Brian Salomon25a88092016-12-01 09:36:50 -05002871 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002872
bsalomon4b4a7cc2016-07-08 04:42:54 -07002873 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2874 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002875 static GrOp::Owner Make(GrRecordingContext* context,
2876 GrPaint&& paint,
2877 const SkMatrix& viewMatrix,
2878 const SkRect& devRect,
2879 float devXRadius,
2880 float devYRadius,
2881 SkVector devStrokeWidths,
2882 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002883 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2884 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002885 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2886 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002887 if (devStrokeWidths.fX > 0) {
2888 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2889 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2890 } else {
2891 devStrokeWidths.scale(SK_ScalarHalf);
2892 }
joshualitt76e7fb62015-02-11 08:52:27 -08002893
bsalomon4b4a7cc2016-07-08 04:42:54 -07002894 // we only handle thick strokes for near-circular ellipses
2895 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002896 (SK_ScalarHalf * devXRadius > devYRadius ||
2897 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002898 return nullptr;
2899 }
2900
2901 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002902 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2903 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002904 return nullptr;
2905 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002906 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2907 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002908 return nullptr;
2909 }
Brian Salomon05441c42017-05-15 16:45:49 -04002910 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002911 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002912 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002913 devXRadius, devYRadius, devStrokeWidths,
2914 strokeOnly);
2915 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002916
Herb Derbyc76d4092020-10-07 16:46:15 -04002917 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002918 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2919 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002920 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002921 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002922 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002923 SkScalar innerXRadius = 0.0f;
2924 SkScalar innerYRadius = 0.0f;
2925 SkRect bounds = devRect;
2926 bool stroked = false;
2927 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002928 // this is legit only if scale & translation (which should be the case at the moment)
2929 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002930 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2931 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002932 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2933 }
2934
Brian Salomon05441c42017-05-15 16:45:49 -04002935 devXRadius += devStrokeHalfWidths.fX;
2936 devYRadius += devStrokeHalfWidths.fY;
2937 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002938 }
2939
Brian Salomon05441c42017-05-15 16:45:49 -04002940 fStroked = stroked;
2941 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002942 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002943 fRRects.emplace_back(
2944 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002945 }
2946
Brian Salomon289e3d82016-12-14 15:52:56 -05002947 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002948
Robert Phillips294723d2021-06-17 09:23:58 -04002949 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002950 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002951 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002952 } else {
2953 fHelper.visitProxies(func);
2954 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002955 }
2956
Chris Dalton57ab06c2021-04-22 12:57:28 -06002957 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2958 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002959 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002960 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002961 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002962 GrProcessorAnalysisCoverage::kSingleChannel, color,
2963 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002964 }
2965
2966 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2967
bsalomone46f9fe2015-08-18 06:05:14 -07002968private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002969 GrProgramInfo* programInfo() override { return fProgramInfo; }
2970
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 void onCreateProgramInfo(const GrCaps* caps,
2972 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002973 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002974 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002975 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002976 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002977 GrXferBarrierFlags renderPassXferBarriers,
2978 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002979 SkMatrix localMatrix;
2980 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002981 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002982 }
2983
Robert Phillips4490d922020-03-03 14:50:59 -05002984 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2985 fUseScale, localMatrix);
2986
Chris Dalton2a26c502021-08-26 10:05:11 -06002987 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
2988 std::move(appliedClip), dstProxyView, gp,
2989 GrPrimitiveType::kTriangles,
Chris Daltoneb0195e2021-08-18 21:39:02 -06002990 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002991 }
2992
Robert Phillips71143952021-06-17 14:55:07 -04002993 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002994 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002995 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002996 if (!fProgramInfo) {
2997 return;
2998 }
2999 }
joshualitt76e7fb62015-02-11 08:52:27 -08003000
bsalomonb5238a72015-05-05 07:49:49 -07003001 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07003002 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04003003 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3004 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003005
Brian Salomon12d22642019-01-29 14:38:50 -05003006 if (!indexBuffer) {
3007 SkDebugf("Could not allocate indices\n");
3008 return;
3009 }
Robert Phillips4490d922020-03-03 14:50:59 -05003010 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003011 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003012 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003013 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003014 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003015 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003016 SkDebugf("Could not allocate vertices\n");
3017 return;
3018 }
3019
Brian Salomon05441c42017-05-15 16:45:49 -04003020 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003021 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003022 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003023 float reciprocalRadii[4] = {
3024 SkScalarInvert(rrect.fXRadius),
3025 SkScalarInvert(rrect.fYRadius),
3026 SkScalarInvert(rrect.fInnerXRadius),
3027 SkScalarInvert(rrect.fInnerYRadius)
3028 };
joshualitt76e7fb62015-02-11 08:52:27 -08003029
Brian Osmane3afdd52020-10-28 10:49:56 -04003030 // If the stroke width is exactly double the radius, the inner radii will be zero.
3031 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3032 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3033 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3034
Chris Dalton25da4062021-07-13 14:06:28 -06003035 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3036 // full sample coverage.
3037 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3038
3039 // Extend out the radii to antialias.
3040 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3041 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003042
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003043 SkScalar xMaxOffset = xOuterRadius;
3044 SkScalar yMaxOffset = yOuterRadius;
3045 if (!fStroked) {
3046 // For filled rrects we map a unit circle in the vertex attributes rather than
3047 // computing an ellipse and modifying that distance, so we normalize to 1.
3048 xMaxOffset /= rrect.fXRadius;
3049 yMaxOffset /= rrect.fYRadius;
3050 }
3051
Chris Dalton25da4062021-07-13 14:06:28 -06003052 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003053
Brian Salomon289e3d82016-12-14 15:52:56 -05003054 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3055 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003056 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003057 SK_ScalarNearlyZero, // we're using inversesqrt() in
3058 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003059 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003060
Brian Osman788b9162020-02-07 10:36:46 -05003061 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003062 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003063 verts.write(bounds.fLeft, yCoords[i],
3064 color,
3065 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003066 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003067 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003068
Brian Osmana1d4eb92018-12-06 16:33:10 -05003069 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3070 color,
3071 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003072 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003073 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003074
Brian Osmana1d4eb92018-12-06 16:33:10 -05003075 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3076 color,
3077 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003078 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003079 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003080
Brian Osmana1d4eb92018-12-06 16:33:10 -05003081 verts.write(bounds.fRight, yCoords[i],
3082 color,
3083 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003084 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003085 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003086 }
3087 }
Robert Phillips4490d922020-03-03 14:50:59 -05003088 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003089 }
3090
3091 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003092 if (!fProgramInfo || !fMesh) {
3093 return;
3094 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003095
Chris Dalton765ed362020-03-16 17:34:44 -06003096 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003097 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003098 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003099 }
3100
Herb Derbye25c3002020-10-27 15:57:27 -04003101 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003102 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003103
Brian Salomon05441c42017-05-15 16:45:49 -04003104 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003105 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003106 }
3107
bsalomoncdaa97b2016-03-08 08:30:14 -08003108 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003109 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003110 }
3111
Brian Salomon05441c42017-05-15 16:45:49 -04003112 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003113 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3114 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003115 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003116 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003117
Brian Salomon05441c42017-05-15 16:45:49 -04003118 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003119 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003120 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003121 }
3122
John Stilesaf366522020-08-13 09:57:34 -04003123#if GR_TEST_UTILS
3124 SkString onDumpInfo() const override {
3125 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3126 for (const auto& geo : fRRects) {
3127 string.appendf(
3128 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3129 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3130 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3131 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3132 geo.fInnerXRadius, geo.fInnerYRadius);
3133 }
3134 string += fHelper.dumpInfo();
3135 return string;
3136 }
3137#endif
3138
Brian Salomon05441c42017-05-15 16:45:49 -04003139 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003140 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003141 SkScalar fXRadius;
3142 SkScalar fYRadius;
3143 SkScalar fInnerXRadius;
3144 SkScalar fInnerYRadius;
3145 SkRect fDevBounds;
3146 };
3147
Brian Salomon289e3d82016-12-14 15:52:56 -05003148 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003149 Helper fHelper;
3150 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003151 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003152 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003153 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003154
Chris Daltoneb694b72020-03-16 09:25:50 -06003155 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003156 GrProgramInfo* fProgramInfo = nullptr;
3157
John Stiles7571f9e2020-09-02 22:42:33 -04003158 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003159};
3160
Herb Derbyc76d4092020-10-07 16:46:15 -04003161GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3162 GrPaint&& paint,
3163 const SkMatrix& viewMatrix,
3164 const SkRRect& rrect,
3165 const SkStrokeRec& stroke,
3166 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003167 SkASSERT(viewMatrix.rectStaysRect());
3168 SkASSERT(viewMatrix.isSimilarity());
3169 SkASSERT(rrect.isSimple());
3170 SkASSERT(!rrect.isOval());
3171 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3172
3173 // RRect ops only handle simple, but not too simple, rrects.
3174 // Do any matrix crunching before we reset the draw state for device coords.
3175 const SkRect& rrectBounds = rrect.getBounds();
3176 SkRect bounds;
3177 viewMatrix.mapRect(&bounds, rrectBounds);
3178
3179 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3180 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3181 viewMatrix[SkMatrix::kMSkewY]));
3182
3183 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3184 SkScalar scaledStroke = -1;
3185 SkScalar strokeWidth = stroke.getWidth();
3186 SkStrokeRec::Style style = stroke.getStyle();
3187
3188 bool isStrokeOnly =
3189 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3190 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3191
3192 if (hasStroke) {
3193 if (SkStrokeRec::kHairline_Style == style) {
3194 scaledStroke = SK_Scalar1;
3195 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003196 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3197 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003198 }
3199 }
3200
3201 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3202 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3203 // patch will have fractional coverage. This only matters when the interior is actually filled.
3204 // We could consider falling back to rect rendering here, since a tiny radius is
3205 // indistinguishable from a square corner.
3206 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3207 return nullptr;
3208 }
3209
3210 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3211 scaledStroke, isStrokeOnly);
3212}
3213
Herb Derbyc76d4092020-10-07 16:46:15 -04003214GrOp::Owner make_rrect_op(GrRecordingContext* context,
3215 GrPaint&& paint,
3216 const SkMatrix& viewMatrix,
3217 const SkRRect& rrect,
3218 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003219 SkASSERT(viewMatrix.rectStaysRect());
3220 SkASSERT(rrect.isSimple());
3221 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003222
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003223 // RRect ops only handle simple, but not too simple, rrects.
3224 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003225 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003226 SkRect bounds;
3227 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003228
Mike Reed242135a2018-02-22 13:41:39 -05003229 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003230 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3231 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3232 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3233 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003234
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003236
bsalomon4b4a7cc2016-07-08 04:42:54 -07003237 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3238 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003239 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003240
Brian Salomon289e3d82016-12-14 15:52:56 -05003241 bool isStrokeOnly =
3242 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003243 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3244
3245 if (hasStroke) {
3246 if (SkStrokeRec::kHairline_Style == style) {
3247 scaledStroke.set(1, 1);
3248 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003249 scaledStroke.fX = SkScalarAbs(
3250 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3251 scaledStroke.fY = SkScalarAbs(
3252 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003253 }
3254
Jim Van Verth64b85892019-06-17 12:01:46 -04003255 // if half of strokewidth is greater than radius, we don't handle that right now
3256 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3257 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003258 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003259 }
3260 }
3261
Brian Salomon8a97f562019-04-18 14:07:27 -04003262 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003263 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003264 std::swap(xRadius, yRadius);
3265 std::swap(scaledStroke.fX, scaledStroke.fY);
3266 }
3267
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003268 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3269 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3270 // patch will have fractional coverage. This only matters when the interior is actually filled.
3271 // We could consider falling back to rect rendering here, since a tiny radius is
3272 // indistinguishable from a square corner.
3273 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003274 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003275 }
3276
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003277 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003278 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3279 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003280}
3281
Herb Derbyc76d4092020-10-07 16:46:15 -04003282GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3283 GrPaint&& paint,
3284 const SkMatrix& viewMatrix,
3285 const SkRRect& rrect,
3286 const SkStrokeRec& stroke,
3287 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003288 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003289 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003290 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003291 }
3292
3293 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003294 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003295 }
3296
Greg Daniel2655ede2019-04-10 00:49:28 +00003297 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003298}
joshualitt3e708c52015-04-30 13:49:27 -07003299
bsalomon4b4a7cc2016-07-08 04:42:54 -07003300///////////////////////////////////////////////////////////////////////////////
3301
Herb Derbyc76d4092020-10-07 16:46:15 -04003302GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3303 GrPaint&& paint,
3304 const SkMatrix& viewMatrix,
3305 const SkRect& oval,
3306 const GrStyle& style,
3307 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003308 SkScalar width = oval.width();
3309 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3310 circle_stays_circle(viewMatrix));
3311
3312 auto r = width / 2.f;
3313 SkPoint center = { oval.centerX(), oval.centerY() };
3314 if (style.hasNonDashPathEffect()) {
3315 return nullptr;
3316 } else if (style.isDashed()) {
3317 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3318 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3319 return nullptr;
3320 }
3321 auto onInterval = style.dashIntervals()[0];
3322 auto offInterval = style.dashIntervals()[1];
3323 if (offInterval == 0) {
3324 GrStyle strokeStyle(style.strokeRec(), nullptr);
3325 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3326 strokeStyle, shaderCaps);
3327 } else if (onInterval == 0) {
3328 // There is nothing to draw but we have no way to indicate that here.
3329 return nullptr;
3330 }
3331 auto angularOnInterval = onInterval / r;
3332 auto angularOffInterval = offInterval / r;
3333 auto phaseAngle = style.dashPhase() / r;
3334 // Currently this function doesn't accept ovals with different start angles, though
3335 // it could.
3336 static const SkScalar kStartAngle = 0.f;
3337 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3338 style.strokeRec().getWidth(), kStartAngle,
3339 angularOnInterval, angularOffInterval, phaseAngle);
3340 }
3341 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3342}
3343
Herb Derbyc76d4092020-10-07 16:46:15 -04003344GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3345 GrPaint&& paint,
3346 const SkMatrix& viewMatrix,
3347 const SkRect& oval,
3348 const GrStyle& style,
3349 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003350 if (style.pathEffect()) {
3351 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003352 }
3353
Stan Ilieveb868aa2017-02-21 11:06:16 -05003354 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003355 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003356 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003357 }
3358
Stan Ilieveb868aa2017-02-21 11:06:16 -05003359 // Otherwise, if we have shader derivative support, render as device-independent
3360 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003361 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3362 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3363 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3364 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3365 // Check for near-degenerate matrix
3366 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003367 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003368 style.strokeRec());
3369 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003370 }
3371
bsalomon4b4a7cc2016-07-08 04:42:54 -07003372 return nullptr;
3373}
3374
3375///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003376
Herb Derbyc76d4092020-10-07 16:46:15 -04003377GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3378 GrPaint&& paint,
3379 const SkMatrix& viewMatrix,
3380 const SkRect& oval, SkScalar startAngle,
3381 SkScalar sweepAngle, bool useCenter,
3382 const GrStyle& style,
3383 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003384 SkASSERT(!oval.isEmpty());
3385 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003386 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003387 if (SkScalarAbs(sweepAngle) >= 360.f) {
3388 return nullptr;
3389 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003390 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3391 return nullptr;
3392 }
3393 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003394 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3395 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003396 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003397 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003398}
3399
3400///////////////////////////////////////////////////////////////////////////////
3401
Hal Canary6f6961e2017-01-31 13:50:44 -05003402#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003403
Brian Salomon05441c42017-05-15 16:45:49 -04003404GR_DRAW_OP_TEST_DEFINE(CircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003405 if (numSamples > 1) {
3406 return nullptr;
3407 }
3408
bsalomon4f3a0ca2016-08-22 13:14:26 -07003409 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003410 SkScalar rotate = random->nextSScalar1() * 360.f;
3411 SkScalar translateX = random->nextSScalar1() * 1000.f;
3412 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003413 SkScalar scale;
3414 do {
3415 scale = random->nextSScalar1() * 100.f;
3416 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003417 SkMatrix viewMatrix;
3418 viewMatrix.setRotate(rotate);
3419 viewMatrix.postTranslate(translateX, translateY);
3420 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003421 SkRect circle = GrTest::TestSquare(random);
3422 SkPoint center = {circle.centerX(), circle.centerY()};
3423 SkScalar radius = circle.width() / 2.f;
3424 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003425 CircleOp::ArcParams arcParamsTmp;
3426 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003427 if (random->nextBool()) {
3428 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003429 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3430 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003431 arcParams = &arcParamsTmp;
3432 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003433 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3434 center, radius,
3435 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003436 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003437 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003438 }
Mike Klein16885072018-12-11 09:54:31 -05003439 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003440 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003441}
3442
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003443GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003444 if (numSamples > 1) {
3445 return nullptr;
3446 }
3447
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003448 SkScalar rotate = random->nextSScalar1() * 360.f;
3449 SkScalar translateX = random->nextSScalar1() * 1000.f;
3450 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003451 SkScalar scale;
3452 do {
3453 scale = random->nextSScalar1() * 100.f;
3454 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003455 SkMatrix viewMatrix;
3456 viewMatrix.setRotate(rotate);
3457 viewMatrix.postTranslate(translateX, translateY);
3458 viewMatrix.postScale(scale, scale);
3459 SkRect circle = GrTest::TestSquare(random);
3460 SkPoint center = {circle.centerX(), circle.centerY()};
3461 SkScalar radius = circle.width() / 2.f;
3462 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3463 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3464 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3465 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3466 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003467 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3468 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003469 startAngle, onAngle, offAngle, phase);
3470}
3471
Brian Salomon05441c42017-05-15 16:45:49 -04003472GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003473 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003474 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003475 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003476 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003477}
3478
Brian Salomon05441c42017-05-15 16:45:49 -04003479GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003480 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003481 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003482 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003483 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003484}
3485
Jim Van Verth64b85892019-06-17 12:01:46 -04003486GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3487 do {
3488 SkScalar rotate = random->nextSScalar1() * 360.f;
3489 SkScalar translateX = random->nextSScalar1() * 1000.f;
3490 SkScalar translateY = random->nextSScalar1() * 1000.f;
3491 SkScalar scale;
3492 do {
3493 scale = random->nextSScalar1() * 100.f;
3494 } while (scale == 0);
3495 SkMatrix viewMatrix;
3496 viewMatrix.setRotate(rotate);
3497 viewMatrix.postTranslate(translateX, translateY);
3498 viewMatrix.postScale(scale, scale);
3499 SkRect rect = GrTest::TestRect(random);
3500 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3501 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3502 if (rrect.isOval()) {
3503 continue;
3504 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003505 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003506 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3507 GrTest::TestStrokeRec(random), nullptr);
3508 if (op) {
3509 return op;
3510 }
3511 assert_alive(paint);
3512 } while (true);
3513}
3514
Brian Salomon05441c42017-05-15 16:45:49 -04003515GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003516 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003517 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003518 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003519 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003520}
3521
3522#endif