blob: 1a30f4a9c815f18a8ff03c0eb3f30d20eefe00fa [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");
735 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:
Greg Daniel2655ede2019-04-10 00:49:28 +0000745 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400746 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400747 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400748 , fViewMatrix(viewMatrix)
749 , fUseScale(useScale)
750 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500751 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500752 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400753 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};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500761 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762 }
763
Brian Salomonbab2d112021-08-11 09:59:56 -0400764 class Impl : public ProgramImpl {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000765 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400766 void setData(const GrGLSLProgramDataManager& pdman,
767 const GrShaderCaps& shaderCaps,
768 const GrGeometryProcessor& geomProc) override {
769 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000770
Brian Salomonbab2d112021-08-11 09:59:56 -0400771 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
772 }
773
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;
egdaniel7ea439b2015-12-03 09:20:44 -0800779 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
joshualittabb52a12015-01-13 15:02:10 -0800798 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400799 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 }
866
Brian Salomonbab2d112021-08-11 09:59:56 -0400867 SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
joshualitt5559ca22015-05-21 15:50:36 -0700868 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
Brian Salomon8afde5f2020-04-01 16:22:00 -04001258 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001259 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001260 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001261 }
1262
Robert Phillips71143952021-06-17 14:55:07 -04001263 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001264 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001265 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001266 if (!fProgramInfo) {
1267 return;
1268 }
1269 }
1270
Brian Salomon12d22642019-01-29 14:38:50 -05001271 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001272 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001273 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001274 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001275 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001276 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001277 return;
1278 }
1279
Brian Salomon12d22642019-01-29 14:38:50 -05001280 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001281 int firstIndex = 0;
1282 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1283 if (!indices) {
1284 SkDebugf("Could not allocate indices\n");
1285 return;
1286 }
1287
1288 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001289 for (const auto& circle : fCircles) {
1290 SkScalar innerRadius = circle.fInnerRadius;
1291 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001292 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001293 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001294
joshualitt76e7fb62015-02-11 08:52:27 -08001295 // The inner radius in the vertex data must be specified in normalized space.
1296 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001297 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001298
1299 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001300 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001301
Brian Osman9a24fee2018-08-03 09:48:42 -04001302 SkVector geoClipPlane = { 0, 0 };
1303 SkScalar offsetClipDist = SK_Scalar1;
1304 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1305 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1306 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1307 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1308 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1309 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1310 // the AA can extend just past the center of the circle.
1311 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1312 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1313 SkAssertResult(geoClipPlane.normalize());
1314 offsetClipDist = 0.5f / halfWidth;
1315 }
1316
Brian Osman7d8f82b2018-11-08 10:24:09 -05001317 for (int i = 0; i < 8; ++i) {
1318 // This clips the normalized offset to the half-plane we computed above. Then we
1319 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001320 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001321 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001322 vertices.write(center + offset * halfWidth,
1323 color,
1324 offset,
1325 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001326 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001327 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001328 }
1329 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001330 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001331 }
1332 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001334 }
1335 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001336 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001337 }
Brian Salomon45c92202018-04-10 10:53:58 -04001338 }
jvanverth6ca48822016-10-07 06:57:32 -07001339
Brian Salomon05441c42017-05-15 16:45:49 -04001340 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001341 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001342
Brian Osman7d8f82b2018-11-08 10:24:09 -05001343 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001344 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1345 color,
1346 kOctagonInner[i] * innerRadius,
1347 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
1354 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
1357 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 }
Brian Salomon45c92202018-04-10 10:53:58 -04001360 }
jvanverth6ca48822016-10-07 06:57:32 -07001361 } else {
1362 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001364 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001366 }
jvanverth6ca48822016-10-07 06:57:32 -07001367 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001369 }
1370 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001372 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001373 if (fRoundCaps) {
1374 vertices.write(circle.fRoundCapCenters);
1375 }
jvanverth6ca48822016-10-07 06:57:32 -07001376 }
1377
Brian Salomon05441c42017-05-15 16:45:49 -04001378 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1379 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001380 for (int i = 0; i < primIndexCount; ++i) {
1381 *indices++ = primIndices[i] + currStartVertex;
1382 }
1383
Brian Salomon05441c42017-05-15 16:45:49 -04001384 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001385 }
jvanverth6ca48822016-10-07 06:57:32 -07001386
Robert Phillips4490d922020-03-03 14:50:59 -05001387 fMesh = target->allocMesh();
1388 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001389 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001390 }
1391
1392 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001393 if (!fProgramInfo || !fMesh) {
1394 return;
1395 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001396
Chris Dalton765ed362020-03-16 17:34:44 -06001397 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001398 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001399 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001400 }
1401
Herb Derbye25c3002020-10-27 15:57:27 -04001402 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001403 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001404
1405 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001406 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001407 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001408 }
1409
Brian Salomon05441c42017-05-15 16:45:49 -04001410 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001411 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001412 }
1413
Brian Salomon05441c42017-05-15 16:45:49 -04001414 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001415 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1416 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001417 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001418 }
1419
Brian Salomon289e3d82016-12-14 15:52:56 -05001420 // Because we've set up the ops that don't use the planes with noop values
1421 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001422 fClipPlane |= that->fClipPlane;
1423 fClipPlaneIsect |= that->fClipPlaneIsect;
1424 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001425 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001426 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001427
Brian Salomon05441c42017-05-15 16:45:49 -04001428 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001429 fVertCount += that->fVertCount;
1430 fIndexCount += that->fIndexCount;
1431 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001432 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001433 }
1434
John Stilesaf366522020-08-13 09:57:34 -04001435#if GR_TEST_UTILS
1436 SkString onDumpInfo() const override {
1437 SkString string;
1438 for (int i = 0; i < fCircles.count(); ++i) {
1439 string.appendf(
1440 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1441 "InnerRad: %.2f, OuterRad: %.2f\n",
1442 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1443 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1444 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1445 fCircles[i].fOuterRadius);
1446 }
1447 string += fHelper.dumpInfo();
1448 return string;
1449 }
1450#endif
1451
Brian Salomon05441c42017-05-15 16:45:49 -04001452 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001453 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 SkScalar fInnerRadius;
1455 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001456 SkScalar fClipPlane[3];
1457 SkScalar fIsectPlane[3];
1458 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001459 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001460 SkRect fDevBounds;
1461 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462 };
1463
Brian Salomon289e3d82016-12-14 15:52:56 -05001464 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001465 Helper fHelper;
1466 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001467 int fVertCount;
1468 int fIndexCount;
1469 bool fAllFill;
1470 bool fClipPlane;
1471 bool fClipPlaneIsect;
1472 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001473 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001474 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001475
Chris Daltoneb694b72020-03-16 09:25:50 -06001476 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001477 GrProgramInfo* fProgramInfo = nullptr;
1478
John Stiles7571f9e2020-09-02 22:42:33 -04001479 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001480};
1481
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001482class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1483private:
1484 using Helper = GrSimpleMeshDrawOpHelper;
1485
1486public:
1487 DEFINE_OP_CLASS_ID
1488
Herb Derbyc76d4092020-10-07 16:46:15 -04001489 static GrOp::Owner Make(GrRecordingContext* context,
1490 GrPaint&& paint,
1491 const SkMatrix& viewMatrix,
1492 SkPoint center,
1493 SkScalar radius,
1494 SkScalar strokeWidth,
1495 SkScalar startAngle,
1496 SkScalar onAngle,
1497 SkScalar offAngle,
1498 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001499 SkASSERT(circle_stays_circle(viewMatrix));
1500 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001501 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1502 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001503 onAngle, offAngle, phaseAngle);
1504 }
1505
Herb Derbyc76d4092020-10-07 16:46:15 -04001506 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001507 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1508 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1509 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001510 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001511 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001512 SkASSERT(circle_stays_circle(viewMatrix));
1513 viewMatrix.mapPoints(&center, 1);
1514 radius = viewMatrix.mapRadius(radius);
1515 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1516
1517 // Determine the angle where the circle starts in device space and whether its orientation
1518 // has been reversed.
1519 SkVector start;
1520 bool reflection;
1521 if (!startAngle) {
1522 start = {1, 0};
1523 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001524 start.fY = SkScalarSin(startAngle);
1525 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526 }
1527 viewMatrix.mapVectors(&start, 1);
1528 startAngle = SkScalarATan2(start.fY, start.fX);
1529 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1530 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1531
1532 auto totalAngle = onAngle + offAngle;
1533 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1534
1535 SkScalar halfWidth = 0;
1536 if (SkScalarNearlyZero(strokeWidth)) {
1537 halfWidth = SK_ScalarHalf;
1538 } else {
1539 halfWidth = SkScalarHalf(strokeWidth);
1540 }
1541
1542 SkScalar outerRadius = radius + halfWidth;
1543 SkScalar innerRadius = radius - halfWidth;
1544
1545 // The radii are outset for two reasons. First, it allows the shader to simply perform
1546 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1547 // Second, the outer radius is used to compute the verts of the bounding box that is
1548 // rendered and the outset ensures the box will cover all partially covered by the circle.
1549 outerRadius += SK_ScalarHalf;
1550 innerRadius -= SK_ScalarHalf;
1551 fViewMatrixIfUsingLocalCoords = viewMatrix;
1552
1553 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1554 center.fX + outerRadius, center.fY + outerRadius);
1555
1556 // We store whether there is a reflection as a negative total angle.
1557 if (reflection) {
1558 totalAngle = -totalAngle;
1559 }
1560 fCircles.push_back(Circle{
1561 color,
1562 outerRadius,
1563 innerRadius,
1564 onAngle,
1565 totalAngle,
1566 startAngle,
1567 phaseAngle,
1568 devBounds
1569 });
1570 // Use the original radius and stroke radius for the bounds so that it does not include the
1571 // AA bloat.
1572 radius += halfWidth;
1573 this->setBounds(
1574 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001575 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001576 fVertCount = circle_type_to_vert_count(true);
1577 fIndexCount = circle_type_to_index_count(true);
1578 }
1579
1580 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1581
Robert Phillips294723d2021-06-17 09:23:58 -04001582 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001583 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001584 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001585 } else {
1586 fHelper.visitProxies(func);
1587 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001588 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001589
Chris Dalton57ab06c2021-04-22 12:57:28 -06001590 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1591 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001592 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001593 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001594 GrProcessorAnalysisCoverage::kSingleChannel, color,
1595 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001596 }
1597
1598 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1599
1600private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001601 GrProgramInfo* programInfo() override { return fProgramInfo; }
1602
Robert Phillips4133dc42020-03-11 15:55:55 -04001603 void onCreateProgramInfo(const GrCaps* caps,
1604 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001605 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001606 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001607 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001608 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001609 GrXferBarrierFlags renderPassXferBarriers,
1610 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001611 SkASSERT(!usesMSAASurface);
1612
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001613 SkMatrix localMatrix;
1614 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001615 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001616 }
1617
1618 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001619 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001620 fWideColor,
1621 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001622
Brian Salomon8afde5f2020-04-01 16:22:00 -04001623 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001624 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001625 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001626 }
1627
Robert Phillips71143952021-06-17 14:55:07 -04001628 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001629 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001630 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001631 if (!fProgramInfo) {
1632 return;
1633 }
1634 }
1635
Brian Salomon12d22642019-01-29 14:38:50 -05001636 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001637 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001638 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001639 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001640 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001641 SkDebugf("Could not allocate vertices\n");
1642 return;
1643 }
1644
Brian Salomon12d22642019-01-29 14:38:50 -05001645 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001646 int firstIndex = 0;
1647 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1648 if (!indices) {
1649 SkDebugf("Could not allocate indices\n");
1650 return;
1651 }
1652
1653 int currStartVertex = 0;
1654 for (const auto& circle : fCircles) {
1655 // The inner radius in the vertex data must be specified in normalized space so that
1656 // length() can be called with smaller values to avoid precision issues with half
1657 // floats.
1658 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1659 const SkRect& bounds = circle.fDevBounds;
1660 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001661 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1662 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1663 };
1664 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001665 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001666 dashParams.totalAngle = -dashParams.totalAngle;
1667 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001668 }
1669
Brian Osmane3caf2d2018-11-21 13:48:36 -05001670 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001671
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001672 // The bounding geometry for the circle is composed of an outer bounding octagon and
1673 // an inner bounded octagon.
1674
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001675 // Compute the vertices of the outer octagon.
1676 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1677 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001678
1679 auto reflectY = [=](const SkPoint& p) {
1680 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001681 };
Brian Osman9d958b52018-11-13 12:46:56 -05001682
1683 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001684 vertices.write(center + kOctagonOuter[i] * halfWidth,
1685 color,
1686 reflectY(kOctagonOuter[i]),
1687 circle.fOuterRadius,
1688 normInnerRadius,
1689 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001690 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001691
1692 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001693 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001694 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1695 color,
1696 reflectY(kOctagonInner[i]) * normInnerRadius,
1697 circle.fOuterRadius,
1698 normInnerRadius,
1699 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001700 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001701
1702 const uint16_t* primIndices = circle_type_to_indices(true);
1703 const int primIndexCount = circle_type_to_index_count(true);
1704 for (int i = 0; i < primIndexCount; ++i) {
1705 *indices++ = primIndices[i] + currStartVertex;
1706 }
1707
1708 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001709 }
1710
Robert Phillips4490d922020-03-03 14:50:59 -05001711 fMesh = target->allocMesh();
1712 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001713 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001714 }
1715
1716 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001717 if (!fProgramInfo || !fMesh) {
1718 return;
1719 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001720
Chris Dalton765ed362020-03-16 17:34:44 -06001721 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001722 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001723 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001724 }
1725
Herb Derbye25c3002020-10-27 15:57:27 -04001726 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001727 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1728
1729 // can only represent 65535 unique vertices with 16-bit indices
1730 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001731 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001732 }
1733
1734 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001735 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001736 }
1737
1738 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001739 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1740 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001741 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001742 }
1743
1744 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001745 fVertCount += that->fVertCount;
1746 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001747 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001748 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001749 }
1750
John Stilesaf366522020-08-13 09:57:34 -04001751#if GR_TEST_UTILS
1752 SkString onDumpInfo() const override {
1753 SkString string;
1754 for (int i = 0; i < fCircles.count(); ++i) {
1755 string.appendf(
1756 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1757 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1758 "Phase: %.2f\n",
1759 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1760 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1761 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1762 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1763 fCircles[i].fPhaseAngle);
1764 }
1765 string += fHelper.dumpInfo();
1766 return string;
1767 }
1768#endif
1769
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001770 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001771 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001772 SkScalar fOuterRadius;
1773 SkScalar fInnerRadius;
1774 SkScalar fOnAngle;
1775 SkScalar fTotalAngle;
1776 SkScalar fStartAngle;
1777 SkScalar fPhaseAngle;
1778 SkRect fDevBounds;
1779 };
1780
1781 SkMatrix fViewMatrixIfUsingLocalCoords;
1782 Helper fHelper;
1783 SkSTArray<1, Circle, true> fCircles;
1784 int fVertCount;
1785 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001786 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001787
Chris Daltoneb694b72020-03-16 09:25:50 -06001788 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001789 GrProgramInfo* fProgramInfo = nullptr;
1790
John Stiles7571f9e2020-09-02 22:42:33 -04001791 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001792};
1793
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001794///////////////////////////////////////////////////////////////////////////////
1795
Brian Salomon05441c42017-05-15 16:45:49 -04001796class EllipseOp : public GrMeshDrawOp {
1797private:
1798 using Helper = GrSimpleMeshDrawOpHelper;
1799
1800 struct DeviceSpaceParams {
1801 SkPoint fCenter;
1802 SkScalar fXRadius;
1803 SkScalar fYRadius;
1804 SkScalar fInnerXRadius;
1805 SkScalar fInnerYRadius;
1806 };
1807
joshualitt76e7fb62015-02-11 08:52:27 -08001808public:
Brian Salomon25a88092016-12-01 09:36:50 -05001809 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001810
Herb Derbyc76d4092020-10-07 16:46:15 -04001811 static GrOp::Owner Make(GrRecordingContext* context,
1812 GrPaint&& paint,
1813 const SkMatrix& viewMatrix,
1814 const SkRect& ellipse,
1815 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001816 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001817 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001818 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1819 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001820 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1821 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001822 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1823 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1824 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1825 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001826
bsalomon4b4a7cc2016-07-08 04:42:54 -07001827 // do (potentially) anisotropic mapping of stroke
1828 SkVector scaledStroke;
1829 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001830 scaledStroke.fX = SkScalarAbs(
1831 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1832 scaledStroke.fY = SkScalarAbs(
1833 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001834
1835 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001836 bool isStrokeOnly =
1837 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001838 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1839
Brian Salomon05441c42017-05-15 16:45:49 -04001840 params.fInnerXRadius = 0;
1841 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001842 if (hasStroke) {
1843 if (SkScalarNearlyZero(scaledStroke.length())) {
1844 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1845 } else {
1846 scaledStroke.scale(SK_ScalarHalf);
1847 }
1848
1849 // we only handle thick strokes for near-circular ellipses
1850 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001851 (0.5f * params.fXRadius > params.fYRadius ||
1852 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001853 return nullptr;
1854 }
1855
1856 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001857 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1858 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1859 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1860 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001861 return nullptr;
1862 }
1863
1864 // this is legit only if scale & translation (which should be the case at the moment)
1865 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001866 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1867 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001868 }
1869
Brian Salomon05441c42017-05-15 16:45:49 -04001870 params.fXRadius += scaledStroke.fX;
1871 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001872 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001873
1874 // For large ovals with low precision floats, we fall back to the path renderer.
1875 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1876 // minimum value to avoid divides by zero. With large ovals and low precision this
1877 // leads to blurring at the edge of the oval.
1878 const SkScalar kMaxOvalRadius = 16384;
1879 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1880 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1881 return nullptr;
1882 }
1883
Greg Daniel2655ede2019-04-10 00:49:28 +00001884 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001885 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001886 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001887
Herb Derbyc76d4092020-10-07 16:46:15 -04001888 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001889 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001890 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001891 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001892 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001893 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001894 SkStrokeRec::Style style = stroke.getStyle();
1895 bool isStrokeOnly =
1896 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001897
Brian Salomon05441c42017-05-15 16:45:49 -04001898 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1899 params.fInnerXRadius, params.fInnerYRadius,
1900 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1901 params.fCenter.fY - params.fYRadius,
1902 params.fCenter.fX + params.fXRadius,
1903 params.fCenter.fY + params.fYRadius)});
1904
Greg Daniel5faf4742019-10-01 15:14:44 -04001905 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001906
Brian Salomon05441c42017-05-15 16:45:49 -04001907 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1908 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001909 }
joshualitt76e7fb62015-02-11 08:52:27 -08001910
Brian Salomon289e3d82016-12-14 15:52:56 -05001911 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001912
Robert Phillips294723d2021-06-17 09:23:58 -04001913 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001914 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001915 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001916 } else {
1917 fHelper.visitProxies(func);
1918 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001919 }
1920
Chris Dalton57ab06c2021-04-22 12:57:28 -06001921 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1922 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001923 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1924 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001925 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001926 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001927 GrProcessorAnalysisCoverage::kSingleChannel, color,
1928 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001929 }
1930
1931 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1932
bsalomone46f9fe2015-08-18 06:05:14 -07001933private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001934 GrProgramInfo* programInfo() override { return fProgramInfo; }
1935
Robert Phillips4133dc42020-03-11 15:55:55 -04001936 void onCreateProgramInfo(const GrCaps* caps,
1937 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001938 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001939 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001940 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001941 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001942 GrXferBarrierFlags renderPassXferBarriers,
1943 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001944 SkMatrix localMatrix;
1945 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001946 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001947 }
1948
Robert Phillips4490d922020-03-03 14:50:59 -05001949 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1950 fUseScale, localMatrix);
1951
Chris Dalton25da4062021-07-13 14:06:28 -06001952 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
1953 if (usesMSAASurface) {
1954 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
1955 }
1956
1957 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
1958 std::move(appliedClip),
1959 dstProxyView, gp,
1960 fHelper.detachProcessorSet(),
1961 GrPrimitiveType::kTriangles,
1962 renderPassXferBarriers,
1963 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05001964 }
1965
Robert Phillips71143952021-06-17 14:55:07 -04001966 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001967 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001968 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001969 if (!fProgramInfo) {
1970 return;
1971 }
1972 }
1973
Robert Phillips787fd9d2021-03-22 14:48:09 -04001974 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001975 GrVertexWriter verts{helper.vertices()};
1976 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001977 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001978 return;
1979 }
1980
Chris Dalton25da4062021-07-13 14:06:28 -06001981 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
1982 // full sample coverage.
1983 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
1984
Brian Salomon05441c42017-05-15 16:45:49 -04001985 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001986 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001987 SkScalar xRadius = ellipse.fXRadius;
1988 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001989
1990 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001991 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1992 SkScalarInvert(xRadius),
1993 SkScalarInvert(yRadius),
1994 SkScalarInvert(ellipse.fInnerXRadius),
1995 SkScalarInvert(ellipse.fInnerYRadius)
1996 };
Chris Dalton25da4062021-07-13 14:06:28 -06001997 SkScalar xMaxOffset = xRadius + aaBloat;
1998 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07001999
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002000 if (!fStroked) {
2001 // For filled ellipses we map a unit circle in the vertex attributes rather than
2002 // computing an ellipse and modifying that distance, so we normalize to 1
2003 xMaxOffset /= xRadius;
2004 yMaxOffset /= yRadius;
2005 }
2006
joshualitt76e7fb62015-02-11 08:52:27 -08002007 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002008 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2009 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002010 color,
2011 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002012 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002013 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002014 }
Robert Phillips4490d922020-03-03 14:50:59 -05002015 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002016 }
2017
2018 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002019 if (!fProgramInfo || !fMesh) {
2020 return;
2021 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002022
Chris Dalton765ed362020-03-16 17:34:44 -06002023 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002024 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002025 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002026 }
2027
Herb Derbye25c3002020-10-27 15:57:27 -04002028 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002029 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002030
Brian Salomon05441c42017-05-15 16:45:49 -04002031 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002032 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002033 }
2034
bsalomoncdaa97b2016-03-08 08:30:14 -08002035 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002036 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002037 }
2038
Brian Salomon05441c42017-05-15 16:45:49 -04002039 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002040 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2041 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002042 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002043 }
2044
Brian Salomon05441c42017-05-15 16:45:49 -04002045 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002046 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002047 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002048 }
2049
John Stilesaf366522020-08-13 09:57:34 -04002050#if GR_TEST_UTILS
2051 SkString onDumpInfo() const override {
2052 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2053 for (const auto& geo : fEllipses) {
2054 string.appendf(
2055 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2056 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2057 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2058 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2059 geo.fInnerXRadius, geo.fInnerYRadius);
2060 }
2061 string += fHelper.dumpInfo();
2062 return string;
2063 }
2064#endif
2065
Brian Salomon05441c42017-05-15 16:45:49 -04002066 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002067 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002068 SkScalar fXRadius;
2069 SkScalar fYRadius;
2070 SkScalar fInnerXRadius;
2071 SkScalar fInnerYRadius;
2072 SkRect fDevBounds;
2073 };
joshualitt76e7fb62015-02-11 08:52:27 -08002074
Brian Salomon289e3d82016-12-14 15:52:56 -05002075 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002076 Helper fHelper;
2077 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002078 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002079 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002080 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002081
Chris Daltoneb694b72020-03-16 09:25:50 -06002082 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002083 GrProgramInfo* fProgramInfo = nullptr;
2084
John Stiles7571f9e2020-09-02 22:42:33 -04002085 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002086};
2087
joshualitt76e7fb62015-02-11 08:52:27 -08002088/////////////////////////////////////////////////////////////////////////////////////////////////
2089
Brian Salomon05441c42017-05-15 16:45:49 -04002090class DIEllipseOp : public GrMeshDrawOp {
2091private:
2092 using Helper = GrSimpleMeshDrawOpHelper;
2093
2094 struct DeviceSpaceParams {
2095 SkPoint fCenter;
2096 SkScalar fXRadius;
2097 SkScalar fYRadius;
2098 SkScalar fInnerXRadius;
2099 SkScalar fInnerYRadius;
2100 DIEllipseStyle fStyle;
2101 };
2102
joshualitt76e7fb62015-02-11 08:52:27 -08002103public:
Brian Salomon25a88092016-12-01 09:36:50 -05002104 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002105
Herb Derbyc76d4092020-10-07 16:46:15 -04002106 static GrOp::Owner Make(GrRecordingContext* context,
2107 GrPaint&& paint,
2108 const SkMatrix& viewMatrix,
2109 const SkRect& ellipse,
2110 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002111 DeviceSpaceParams params;
2112 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2113 params.fXRadius = SkScalarHalf(ellipse.width());
2114 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002115
bsalomon4b4a7cc2016-07-08 04:42:54 -07002116 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002117 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2118 ? DIEllipseStyle::kStroke
2119 : (SkStrokeRec::kHairline_Style == style)
2120 ? DIEllipseStyle::kHairline
2121 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002122
Brian Salomon05441c42017-05-15 16:45:49 -04002123 params.fInnerXRadius = 0;
2124 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002125 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2126 SkScalar strokeWidth = stroke.getWidth();
2127
2128 if (SkScalarNearlyZero(strokeWidth)) {
2129 strokeWidth = SK_ScalarHalf;
2130 } else {
2131 strokeWidth *= SK_ScalarHalf;
2132 }
2133
2134 // we only handle thick strokes for near-circular ellipses
2135 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002136 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2137 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002138 return nullptr;
2139 }
2140
2141 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002142 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2143 (strokeWidth * strokeWidth) * params.fXRadius) {
2144 return nullptr;
2145 }
2146 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2147 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002148 return nullptr;
2149 }
2150
2151 // set inner radius (if needed)
2152 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002153 params.fInnerXRadius = params.fXRadius - strokeWidth;
2154 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002155 }
2156
Brian Salomon05441c42017-05-15 16:45:49 -04002157 params.fXRadius += strokeWidth;
2158 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002159 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002160
2161 // For large ovals with low precision floats, we fall back to the path renderer.
2162 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2163 // minimum value to avoid divides by zero. With large ovals and low precision this
2164 // leads to blurring at the edge of the oval.
2165 const SkScalar kMaxOvalRadius = 16384;
2166 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2167 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2168 return nullptr;
2169 }
2170
Brian Salomon05441c42017-05-15 16:45:49 -04002171 if (DIEllipseStyle::kStroke == params.fStyle &&
2172 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2173 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002174 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002175 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002176 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002177
Herb Derbyc76d4092020-10-07 16:46:15 -04002178 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002179 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002180 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002181 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002182 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002183 // This expands the outer rect so that after CTM we end up with a half-pixel border
2184 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2185 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2186 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2187 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002188 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2189 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002190
Brian Salomon05441c42017-05-15 16:45:49 -04002191 fEllipses.emplace_back(
2192 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2193 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002194 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2195 params.fCenter.fY - params.fYRadius,
2196 params.fCenter.fX + params.fXRadius,
2197 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002198 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002199 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002200 }
2201
Brian Salomon289e3d82016-12-14 15:52:56 -05002202 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002203
Robert Phillips294723d2021-06-17 09:23:58 -04002204 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002205 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002206 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002207 } else {
2208 fHelper.visitProxies(func);
2209 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002210 }
2211
Chris Dalton57ab06c2021-04-22 12:57:28 -06002212 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2213 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002214 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2215 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002216 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002217 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002218 GrProcessorAnalysisCoverage::kSingleChannel, color,
2219 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002220 }
2221
2222 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2223
bsalomone46f9fe2015-08-18 06:05:14 -07002224private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002225 GrProgramInfo* programInfo() override { return fProgramInfo; }
2226
Robert Phillips4133dc42020-03-11 15:55:55 -04002227 void onCreateProgramInfo(const GrCaps* caps,
2228 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002229 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002230 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002231 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002232 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002233 GrXferBarrierFlags renderPassXferBarriers,
2234 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002235 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2236 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002237 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002238
Chris Dalton25da4062021-07-13 14:06:28 -06002239 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2240 if (usesMSAASurface) {
2241 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2242 }
2243
2244 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2245 std::move(appliedClip),
2246 dstProxyView, gp,
2247 fHelper.detachProcessorSet(),
2248 GrPrimitiveType::kTriangles,
2249 renderPassXferBarriers,
2250 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002251 }
2252
Robert Phillips71143952021-06-17 14:55:07 -04002253 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002254 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002255 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002256 }
2257
Robert Phillips787fd9d2021-03-22 14:48:09 -04002258 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002259 GrVertexWriter verts{helper.vertices()};
2260 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002261 return;
2262 }
2263
Brian Salomon05441c42017-05-15 16:45:49 -04002264 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002265 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002266 SkScalar xRadius = ellipse.fXRadius;
2267 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002268
Chris Dalton25da4062021-07-13 14:06:28 -06002269 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2270 // full sample coverage.
2271 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2272 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2273 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002274
Chris Dalton25da4062021-07-13 14:06:28 -06002275 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2276 // occurs at x^2 + y^2 == 1.
2277 float outerCoordX = drawBounds.width() / (xRadius * 2);
2278 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002279
Chris Dalton25da4062021-07-13 14:06:28 -06002280 // By default, constructed so that inner coord is (0, 0) for all points
2281 float innerCoordX = 0;
2282 float innerCoordY = 0;
2283
2284 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2285 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002286 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002287 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2288 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002289 }
joshualitt76e7fb62015-02-11 08:52:27 -08002290
Chris Dalton25da4062021-07-13 14:06:28 -06002291 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002292 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002293 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002294 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002295 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002296 }
Robert Phillips4490d922020-03-03 14:50:59 -05002297 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002298 }
2299
2300 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002301 if (!fProgramInfo || !fMesh) {
2302 return;
2303 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002304
Chris Dalton765ed362020-03-16 17:34:44 -06002305 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002306 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002307 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002308 }
halcanary9d524f22016-03-29 09:03:52 -07002309
Herb Derbye25c3002020-10-27 15:57:27 -04002310 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002311 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002312 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002313 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002314 }
2315
bsalomoncdaa97b2016-03-08 08:30:14 -08002316 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002317 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002318 }
2319
joshualittd96a67b2015-05-05 14:09:05 -07002320 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002321 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002322 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002323 }
2324
Brian Salomon05441c42017-05-15 16:45:49 -04002325 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002326 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002327 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002328 }
2329
John Stilesaf366522020-08-13 09:57:34 -04002330#if GR_TEST_UTILS
2331 SkString onDumpInfo() const override {
2332 SkString string;
2333 for (const auto& geo : fEllipses) {
2334 string.appendf(
2335 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2336 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2337 "GeoDY: %.2f\n",
2338 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2339 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2340 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2341 }
2342 string += fHelper.dumpInfo();
2343 return string;
2344 }
2345#endif
2346
Brian Salomon05441c42017-05-15 16:45:49 -04002347 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2348 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002349
Brian Salomon05441c42017-05-15 16:45:49 -04002350 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002351 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002352 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002353 SkScalar fXRadius;
2354 SkScalar fYRadius;
2355 SkScalar fInnerXRadius;
2356 SkScalar fInnerYRadius;
2357 SkScalar fGeoDx;
2358 SkScalar fGeoDy;
2359 DIEllipseStyle fStyle;
2360 SkRect fBounds;
2361 };
2362
Brian Salomon05441c42017-05-15 16:45:49 -04002363 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002364 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002365 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002366 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002367
Chris Daltoneb694b72020-03-16 09:25:50 -06002368 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002369 GrProgramInfo* fProgramInfo = nullptr;
2370
John Stiles7571f9e2020-09-02 22:42:33 -04002371 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002372};
2373
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002374///////////////////////////////////////////////////////////////////////////////
2375
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002376// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002377//
2378// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2379// ____________
2380// |_|________|_|
2381// | | | |
2382// | | | |
2383// | | | |
2384// |_|________|_|
2385// |_|________|_|
2386//
2387// For strokes, we don't draw the center quad.
2388//
2389// For circular roundrects, in the case where the stroke width is greater than twice
2390// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002391// in the center. The shared vertices are duplicated so we can set a different outer radius
2392// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002393// ____________
2394// |_|________|_|
2395// | |\ ____ /| |
2396// | | | | | |
2397// | | |____| | |
2398// |_|/______\|_|
2399// |_|________|_|
2400//
2401// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002402//
2403// For filled rrects that need to provide a distance vector we resuse the overstroke
2404// geometry but make the inner rect degenerate (either a point or a horizontal or
2405// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002406
jvanverth84839f62016-08-29 10:16:40 -07002407static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002408 // clang-format off
2409 // overstroke quads
2410 // we place this at the beginning so that we can skip these indices when rendering normally
2411 16, 17, 19, 16, 19, 18,
2412 19, 17, 23, 19, 23, 21,
2413 21, 23, 22, 21, 22, 20,
2414 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002415
Brian Salomon289e3d82016-12-14 15:52:56 -05002416 // corners
2417 0, 1, 5, 0, 5, 4,
2418 2, 3, 7, 2, 7, 6,
2419 8, 9, 13, 8, 13, 12,
2420 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002421
Brian Salomon289e3d82016-12-14 15:52:56 -05002422 // edges
2423 1, 2, 6, 1, 6, 5,
2424 4, 5, 9, 4, 9, 8,
2425 6, 7, 11, 6, 11, 10,
2426 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002427
Brian Salomon289e3d82016-12-14 15:52:56 -05002428 // center
2429 // we place this at the end so that we can ignore these indices when not rendering as filled
2430 5, 6, 10, 5, 10, 9,
2431 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002432};
Brian Salomon289e3d82016-12-14 15:52:56 -05002433
jvanverth84839f62016-08-29 10:16:40 -07002434// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002435static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002436
jvanverth84839f62016-08-29 10:16:40 -07002437// overstroke count is arraysize minus the center indices
2438static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2439// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002440static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002441// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002442static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2443static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002444static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002445
jvanverthc3d0e422016-08-25 08:12:35 -07002446enum RRectType {
2447 kFill_RRectType,
2448 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002449 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002450};
2451
jvanverth84839f62016-08-29 10:16:40 -07002452static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002453 switch (type) {
2454 case kFill_RRectType:
2455 case kStroke_RRectType:
2456 return kVertsPerStandardRRect;
2457 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002458 return kVertsPerOverstrokeRRect;
2459 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002460 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002461}
2462
2463static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002464 switch (type) {
2465 case kFill_RRectType:
2466 return kIndicesPerFillRRect;
2467 case kStroke_RRectType:
2468 return kIndicesPerStrokeRRect;
2469 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002470 return kIndicesPerOverstrokeRRect;
2471 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002472 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002473}
2474
2475static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002476 switch (type) {
2477 case kFill_RRectType:
2478 case kStroke_RRectType:
2479 return gStandardRRectIndices;
2480 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002481 return gOverstrokeRRectIndices;
2482 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002483 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002484}
2485
joshualitt76e7fb62015-02-11 08:52:27 -08002486///////////////////////////////////////////////////////////////////////////////////////////////////
2487
Robert Phillips79839d42016-10-06 15:03:34 -04002488// For distance computations in the interior of filled rrects we:
2489//
2490// add a interior degenerate (point or line) rect
2491// each vertex of that rect gets -outerRad as its radius
2492// this makes the computation of the distance to the outer edge be negative
2493// negative values are caught and then handled differently in the GP's onEmitCode
2494// each vertex is also given the normalized x & y distance from the interior rect's edge
2495// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2496
Brian Salomon05441c42017-05-15 16:45:49 -04002497class CircularRRectOp : public GrMeshDrawOp {
2498private:
2499 using Helper = GrSimpleMeshDrawOpHelper;
2500
joshualitt76e7fb62015-02-11 08:52:27 -08002501public:
Brian Salomon25a88092016-12-01 09:36:50 -05002502 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002503
bsalomon4b4a7cc2016-07-08 04:42:54 -07002504 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2505 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002506 static GrOp::Owner Make(GrRecordingContext* context,
2507 GrPaint&& paint,
2508 const SkMatrix& viewMatrix,
2509 const SkRect& devRect,
2510 float devRadius,
2511 float devStrokeWidth,
2512 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002513 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002514 devRect, devRadius,
2515 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002516 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002517 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002518 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2519 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002520 : INHERITED(ClassID())
2521 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002522 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002523 SkRect bounds = devRect;
2524 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2525 SkScalar innerRadius = 0.0f;
2526 SkScalar outerRadius = devRadius;
2527 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002528 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002529 if (devStrokeWidth > 0) {
2530 if (SkScalarNearlyZero(devStrokeWidth)) {
2531 halfWidth = SK_ScalarHalf;
2532 } else {
2533 halfWidth = SkScalarHalf(devStrokeWidth);
2534 }
joshualitt76e7fb62015-02-11 08:52:27 -08002535
bsalomon4b4a7cc2016-07-08 04:42:54 -07002536 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002537 // Outset stroke by 1/4 pixel
2538 devStrokeWidth += 0.25f;
2539 // If stroke is greater than width or height, this is still a fill
2540 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002541 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002542 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002543 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002544 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002545 }
2546 outerRadius += halfWidth;
2547 bounds.outset(halfWidth, halfWidth);
2548 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002549
Greg Daniel2655ede2019-04-10 00:49:28 +00002550 // The radii are outset for two reasons. First, it allows the shader to simply perform
2551 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2552 // Second, the outer radius is used to compute the verts of the bounding box that is
2553 // rendered and the outset ensures the box will cover all partially covered by the rrect
2554 // corners.
2555 outerRadius += SK_ScalarHalf;
2556 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002557
Greg Daniel5faf4742019-10-01 15:14:44 -04002558 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002559
Greg Daniel2655ede2019-04-10 00:49:28 +00002560 // Expand the rect for aa to generate correct vertices.
2561 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002562
Brian Salomon05441c42017-05-15 16:45:49 -04002563 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002564 fVertCount = rrect_type_to_vert_count(type);
2565 fIndexCount = rrect_type_to_index_count(type);
2566 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002567 }
2568
Brian Salomon289e3d82016-12-14 15:52:56 -05002569 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002570
Robert Phillips294723d2021-06-17 09:23:58 -04002571 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002572 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002573 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002574 } else {
2575 fHelper.visitProxies(func);
2576 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002577 }
2578
Chris Dalton57ab06c2021-04-22 12:57:28 -06002579 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2580 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002581 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002582 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002583 GrProcessorAnalysisCoverage::kSingleChannel, color,
2584 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002585 }
2586
2587 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2588
Brian Salomon92aee3d2016-12-21 09:20:25 -05002589private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002590 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002591 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002592 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002593 SkASSERT(smInset < bigInset);
2594
2595 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002596 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2597 color,
2598 xOffset, 0.0f,
2599 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002600
2601 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002602 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2603 color,
2604 xOffset, 0.0f,
2605 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002606
Brian Osmana1d4eb92018-12-06 16:33:10 -05002607 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2608 color,
2609 0.0f, 0.0f,
2610 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002611
Brian Osmana1d4eb92018-12-06 16:33:10 -05002612 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2613 color,
2614 0.0f, 0.0f,
2615 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002616
Brian Osmana1d4eb92018-12-06 16:33:10 -05002617 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2618 color,
2619 0.0f, 0.0f,
2620 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002621
Brian Osmana1d4eb92018-12-06 16:33:10 -05002622 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2623 color,
2624 0.0f, 0.0f,
2625 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002626
2627 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002628 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2629 color,
2630 xOffset, 0.0f,
2631 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002632
2633 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002634 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2635 color,
2636 xOffset, 0.0f,
2637 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002638 }
2639
Robert Phillips2669a7b2020-03-12 12:07:19 -04002640 GrProgramInfo* programInfo() override { return fProgramInfo; }
2641
Robert Phillips4133dc42020-03-11 15:55:55 -04002642 void onCreateProgramInfo(const GrCaps* caps,
2643 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002644 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002645 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002646 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002647 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002648 GrXferBarrierFlags renderPassXferBarriers,
2649 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002650 SkASSERT(!usesMSAASurface);
2651
bsalomoncdaa97b2016-03-08 08:30:14 -08002652 // Invert the view matrix as a local matrix (if any other processors require coords).
2653 SkMatrix localMatrix;
2654 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002655 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002656 }
2657
Robert Phillips4490d922020-03-03 14:50:59 -05002658 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002659 false, false, false, false,
2660 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002661
Brian Salomon8afde5f2020-04-01 16:22:00 -04002662 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002663 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002664 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002665 }
2666
Robert Phillips71143952021-06-17 14:55:07 -04002667 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002668 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002669 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002670 if (!fProgramInfo) {
2671 return;
2672 }
2673 }
2674
Brian Salomon12d22642019-01-29 14:38:50 -05002675 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002676 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002677
Robert Phillips787fd9d2021-03-22 14:48:09 -04002678 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002679 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002680 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002681 SkDebugf("Could not allocate vertices\n");
2682 return;
2683 }
2684
Brian Salomon12d22642019-01-29 14:38:50 -05002685 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002686 int firstIndex = 0;
2687 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2688 if (!indices) {
2689 SkDebugf("Could not allocate indices\n");
2690 return;
2691 }
2692
2693 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002694 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002695 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002696 SkScalar outerRadius = rrect.fOuterRadius;
2697 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002698
Brian Salomon289e3d82016-12-14 15:52:56 -05002699 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2700 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002701
Brian Salomon289e3d82016-12-14 15:52:56 -05002702 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002703 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002704 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002705 SkScalar innerRadius = rrect.fType != kFill_RRectType
2706 ? rrect.fInnerRadius / rrect.fOuterRadius
2707 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002708 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002709 verts.write(bounds.fLeft, yCoords[i],
2710 color,
2711 -1.0f, yOuterRadii[i],
2712 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002713
Brian Osmana1d4eb92018-12-06 16:33:10 -05002714 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2715 color,
2716 0.0f, yOuterRadii[i],
2717 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002718
Brian Osmana1d4eb92018-12-06 16:33:10 -05002719 verts.write(bounds.fRight - 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, yCoords[i],
2725 color,
2726 1.0f, yOuterRadii[i],
2727 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002728 }
jvanverthc3d0e422016-08-25 08:12:35 -07002729 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002730 // Effectively this is an additional stroked rrect, with its
2731 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2732 // This will give us correct AA in the center and the correct
2733 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002734 //
jvanvertha4f1af82016-08-29 07:17:47 -07002735 // Also, the outer offset is a constant vector pointing to the right, which
2736 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002737 if (kOverstroke_RRectType == rrect.fType) {
2738 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002739
Brian Salomon05441c42017-05-15 16:45:49 -04002740 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002741 // this is the normalized distance from the outer rectangle of this
2742 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002743 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002744
Brian Osmana1d4eb92018-12-06 16:33:10 -05002745 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002746 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002747 }
jvanverth6a397612016-08-26 08:15:33 -07002748
Brian Salomon05441c42017-05-15 16:45:49 -04002749 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2750 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002751 for (int i = 0; i < primIndexCount; ++i) {
2752 *indices++ = primIndices[i] + currStartVertex;
2753 }
2754
Brian Salomon05441c42017-05-15 16:45:49 -04002755 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002756 }
2757
Robert Phillips4490d922020-03-03 14:50:59 -05002758 fMesh = target->allocMesh();
2759 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002760 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002761 }
2762
2763 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002764 if (!fProgramInfo || !fMesh) {
2765 return;
2766 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002767
Chris Dalton765ed362020-03-16 17:34:44 -06002768 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002769 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002770 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002771 }
2772
Herb Derbye25c3002020-10-27 15:57:27 -04002773 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002774 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002775
2776 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002777 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002778 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002779 }
2780
Brian Salomon05441c42017-05-15 16:45:49 -04002781 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002782 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002783 }
2784
Brian Salomon05441c42017-05-15 16:45:49 -04002785 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002786 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2787 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002788 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002789 }
2790
Brian Salomon05441c42017-05-15 16:45:49 -04002791 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002792 fVertCount += that->fVertCount;
2793 fIndexCount += that->fIndexCount;
2794 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002795 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002796 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002797 }
2798
John Stilesaf366522020-08-13 09:57:34 -04002799#if GR_TEST_UTILS
2800 SkString onDumpInfo() const override {
2801 SkString string;
2802 for (int i = 0; i < fRRects.count(); ++i) {
2803 string.appendf(
2804 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2805 "InnerRad: %.2f, OuterRad: %.2f\n",
2806 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2807 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2808 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2809 fRRects[i].fOuterRadius);
2810 }
2811 string += fHelper.dumpInfo();
2812 return string;
2813 }
2814#endif
2815
Brian Salomon05441c42017-05-15 16:45:49 -04002816 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002817 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002818 SkScalar fInnerRadius;
2819 SkScalar fOuterRadius;
2820 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002821 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002822 };
2823
Brian Salomon289e3d82016-12-14 15:52:56 -05002824 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002825 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002826 int fVertCount;
2827 int fIndexCount;
2828 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002829 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002830 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002831
Chris Daltoneb694b72020-03-16 09:25:50 -06002832 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002833 GrProgramInfo* fProgramInfo = nullptr;
2834
John Stiles7571f9e2020-09-02 22:42:33 -04002835 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002836};
2837
jvanverth84839f62016-08-29 10:16:40 -07002838static const int kNumRRectsInIndexBuffer = 256;
2839
2840GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2841GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002842static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2843 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002844 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2845 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2846 switch (type) {
2847 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002848 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002849 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2850 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002851 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002852 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002853 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2854 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002855 default:
2856 SkASSERT(false);
2857 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002858 }
jvanverth84839f62016-08-29 10:16:40 -07002859}
2860
Brian Salomon05441c42017-05-15 16:45:49 -04002861class EllipticalRRectOp : public GrMeshDrawOp {
2862private:
2863 using Helper = GrSimpleMeshDrawOpHelper;
2864
joshualitt76e7fb62015-02-11 08:52:27 -08002865public:
Brian Salomon25a88092016-12-01 09:36:50 -05002866 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002867
bsalomon4b4a7cc2016-07-08 04:42:54 -07002868 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2869 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002870 static GrOp::Owner Make(GrRecordingContext* context,
2871 GrPaint&& paint,
2872 const SkMatrix& viewMatrix,
2873 const SkRect& devRect,
2874 float devXRadius,
2875 float devYRadius,
2876 SkVector devStrokeWidths,
2877 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002878 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2879 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002880 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2881 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002882 if (devStrokeWidths.fX > 0) {
2883 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2884 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2885 } else {
2886 devStrokeWidths.scale(SK_ScalarHalf);
2887 }
joshualitt76e7fb62015-02-11 08:52:27 -08002888
bsalomon4b4a7cc2016-07-08 04:42:54 -07002889 // we only handle thick strokes for near-circular ellipses
2890 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002891 (SK_ScalarHalf * devXRadius > devYRadius ||
2892 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 return nullptr;
2894 }
2895
2896 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002897 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2898 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002899 return nullptr;
2900 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002901 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2902 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903 return nullptr;
2904 }
Brian Salomon05441c42017-05-15 16:45:49 -04002905 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002906 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002907 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002908 devXRadius, devYRadius, devStrokeWidths,
2909 strokeOnly);
2910 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002911
Herb Derbyc76d4092020-10-07 16:46:15 -04002912 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002913 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2914 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002915 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002916 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002917 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002918 SkScalar innerXRadius = 0.0f;
2919 SkScalar innerYRadius = 0.0f;
2920 SkRect bounds = devRect;
2921 bool stroked = false;
2922 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002923 // this is legit only if scale & translation (which should be the case at the moment)
2924 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002925 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2926 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002927 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2928 }
2929
Brian Salomon05441c42017-05-15 16:45:49 -04002930 devXRadius += devStrokeHalfWidths.fX;
2931 devYRadius += devStrokeHalfWidths.fY;
2932 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002933 }
2934
Brian Salomon05441c42017-05-15 16:45:49 -04002935 fStroked = stroked;
2936 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002937 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002938 fRRects.emplace_back(
2939 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002940 }
2941
Brian Salomon289e3d82016-12-14 15:52:56 -05002942 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002943
Robert Phillips294723d2021-06-17 09:23:58 -04002944 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002945 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002946 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002947 } else {
2948 fHelper.visitProxies(func);
2949 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002950 }
2951
Chris Dalton57ab06c2021-04-22 12:57:28 -06002952 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2953 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002954 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002955 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002956 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002957 GrProcessorAnalysisCoverage::kSingleChannel, color,
2958 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002959 }
2960
2961 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2962
bsalomone46f9fe2015-08-18 06:05:14 -07002963private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002964 GrProgramInfo* programInfo() override { return fProgramInfo; }
2965
Robert Phillips4133dc42020-03-11 15:55:55 -04002966 void onCreateProgramInfo(const GrCaps* caps,
2967 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002968 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002969 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002970 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002971 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002972 GrXferBarrierFlags renderPassXferBarriers,
2973 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002974 SkMatrix localMatrix;
2975 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002976 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002977 }
2978
Robert Phillips4490d922020-03-03 14:50:59 -05002979 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2980 fUseScale, localMatrix);
2981
Chris Dalton25da4062021-07-13 14:06:28 -06002982 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2983 if (usesMSAASurface) {
2984 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2985 }
2986
2987 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2988 std::move(appliedClip),
2989 dstProxyView, gp,
2990 fHelper.detachProcessorSet(),
2991 GrPrimitiveType::kTriangles,
2992 renderPassXferBarriers,
2993 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002994 }
2995
Robert Phillips71143952021-06-17 14:55:07 -04002996 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002997 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002998 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002999 if (!fProgramInfo) {
3000 return;
3001 }
3002 }
joshualitt76e7fb62015-02-11 08:52:27 -08003003
bsalomonb5238a72015-05-05 07:49:49 -07003004 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07003005 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04003006 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3007 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003008
Brian Salomon12d22642019-01-29 14:38:50 -05003009 if (!indexBuffer) {
3010 SkDebugf("Could not allocate indices\n");
3011 return;
3012 }
Robert Phillips4490d922020-03-03 14:50:59 -05003013 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003014 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003015 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003016 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003017 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003018 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003019 SkDebugf("Could not allocate vertices\n");
3020 return;
3021 }
3022
Brian Salomon05441c42017-05-15 16:45:49 -04003023 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003024 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003025 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003026 float reciprocalRadii[4] = {
3027 SkScalarInvert(rrect.fXRadius),
3028 SkScalarInvert(rrect.fYRadius),
3029 SkScalarInvert(rrect.fInnerXRadius),
3030 SkScalarInvert(rrect.fInnerYRadius)
3031 };
joshualitt76e7fb62015-02-11 08:52:27 -08003032
Brian Osmane3afdd52020-10-28 10:49:56 -04003033 // If the stroke width is exactly double the radius, the inner radii will be zero.
3034 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3035 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3036 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3037
Chris Dalton25da4062021-07-13 14:06:28 -06003038 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3039 // full sample coverage.
3040 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3041
3042 // Extend out the radii to antialias.
3043 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3044 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003045
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003046 SkScalar xMaxOffset = xOuterRadius;
3047 SkScalar yMaxOffset = yOuterRadius;
3048 if (!fStroked) {
3049 // For filled rrects we map a unit circle in the vertex attributes rather than
3050 // computing an ellipse and modifying that distance, so we normalize to 1.
3051 xMaxOffset /= rrect.fXRadius;
3052 yMaxOffset /= rrect.fYRadius;
3053 }
3054
Chris Dalton25da4062021-07-13 14:06:28 -06003055 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003056
Brian Salomon289e3d82016-12-14 15:52:56 -05003057 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3058 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003059 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003060 SK_ScalarNearlyZero, // we're using inversesqrt() in
3061 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003062 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003063
Brian Osman788b9162020-02-07 10:36:46 -05003064 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003065 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003066 verts.write(bounds.fLeft, yCoords[i],
3067 color,
3068 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003069 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003070 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003071
Brian Osmana1d4eb92018-12-06 16:33:10 -05003072 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3073 color,
3074 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003075 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003076 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003077
Brian Osmana1d4eb92018-12-06 16:33:10 -05003078 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3079 color,
3080 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003081 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003082 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003083
Brian Osmana1d4eb92018-12-06 16:33:10 -05003084 verts.write(bounds.fRight, yCoords[i],
3085 color,
3086 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003087 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003088 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003089 }
3090 }
Robert Phillips4490d922020-03-03 14:50:59 -05003091 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003092 }
3093
3094 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003095 if (!fProgramInfo || !fMesh) {
3096 return;
3097 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003098
Chris Dalton765ed362020-03-16 17:34:44 -06003099 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003100 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003101 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003102 }
3103
Herb Derbye25c3002020-10-27 15:57:27 -04003104 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003105 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003106
Brian Salomon05441c42017-05-15 16:45:49 -04003107 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003108 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003109 }
3110
bsalomoncdaa97b2016-03-08 08:30:14 -08003111 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003112 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003113 }
3114
Brian Salomon05441c42017-05-15 16:45:49 -04003115 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003116 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3117 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003118 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003119 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003120
Brian Salomon05441c42017-05-15 16:45:49 -04003121 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003122 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003123 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003124 }
3125
John Stilesaf366522020-08-13 09:57:34 -04003126#if GR_TEST_UTILS
3127 SkString onDumpInfo() const override {
3128 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3129 for (const auto& geo : fRRects) {
3130 string.appendf(
3131 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3132 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3133 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3134 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3135 geo.fInnerXRadius, geo.fInnerYRadius);
3136 }
3137 string += fHelper.dumpInfo();
3138 return string;
3139 }
3140#endif
3141
Brian Salomon05441c42017-05-15 16:45:49 -04003142 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003143 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003144 SkScalar fXRadius;
3145 SkScalar fYRadius;
3146 SkScalar fInnerXRadius;
3147 SkScalar fInnerYRadius;
3148 SkRect fDevBounds;
3149 };
3150
Brian Salomon289e3d82016-12-14 15:52:56 -05003151 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003152 Helper fHelper;
3153 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003154 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003155 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003156 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003157
Chris Daltoneb694b72020-03-16 09:25:50 -06003158 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003159 GrProgramInfo* fProgramInfo = nullptr;
3160
John Stiles7571f9e2020-09-02 22:42:33 -04003161 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003162};
3163
Herb Derbyc76d4092020-10-07 16:46:15 -04003164GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3165 GrPaint&& paint,
3166 const SkMatrix& viewMatrix,
3167 const SkRRect& rrect,
3168 const SkStrokeRec& stroke,
3169 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003170 SkASSERT(viewMatrix.rectStaysRect());
3171 SkASSERT(viewMatrix.isSimilarity());
3172 SkASSERT(rrect.isSimple());
3173 SkASSERT(!rrect.isOval());
3174 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3175
3176 // RRect ops only handle simple, but not too simple, rrects.
3177 // Do any matrix crunching before we reset the draw state for device coords.
3178 const SkRect& rrectBounds = rrect.getBounds();
3179 SkRect bounds;
3180 viewMatrix.mapRect(&bounds, rrectBounds);
3181
3182 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3183 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3184 viewMatrix[SkMatrix::kMSkewY]));
3185
3186 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3187 SkScalar scaledStroke = -1;
3188 SkScalar strokeWidth = stroke.getWidth();
3189 SkStrokeRec::Style style = stroke.getStyle();
3190
3191 bool isStrokeOnly =
3192 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3193 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3194
3195 if (hasStroke) {
3196 if (SkStrokeRec::kHairline_Style == style) {
3197 scaledStroke = SK_Scalar1;
3198 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003199 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3200 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003201 }
3202 }
3203
3204 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3205 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3206 // patch will have fractional coverage. This only matters when the interior is actually filled.
3207 // We could consider falling back to rect rendering here, since a tiny radius is
3208 // indistinguishable from a square corner.
3209 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3210 return nullptr;
3211 }
3212
3213 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3214 scaledStroke, isStrokeOnly);
3215}
3216
Herb Derbyc76d4092020-10-07 16:46:15 -04003217GrOp::Owner make_rrect_op(GrRecordingContext* context,
3218 GrPaint&& paint,
3219 const SkMatrix& viewMatrix,
3220 const SkRRect& rrect,
3221 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003222 SkASSERT(viewMatrix.rectStaysRect());
3223 SkASSERT(rrect.isSimple());
3224 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003225
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003226 // RRect ops only handle simple, but not too simple, rrects.
3227 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003228 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003229 SkRect bounds;
3230 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003231
Mike Reed242135a2018-02-22 13:41:39 -05003232 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003233 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3234 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3235 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3236 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003237
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003238 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003239
bsalomon4b4a7cc2016-07-08 04:42:54 -07003240 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3241 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003242 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003243
Brian Salomon289e3d82016-12-14 15:52:56 -05003244 bool isStrokeOnly =
3245 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003246 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3247
3248 if (hasStroke) {
3249 if (SkStrokeRec::kHairline_Style == style) {
3250 scaledStroke.set(1, 1);
3251 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003252 scaledStroke.fX = SkScalarAbs(
3253 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3254 scaledStroke.fY = SkScalarAbs(
3255 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003256 }
3257
Jim Van Verth64b85892019-06-17 12:01:46 -04003258 // if half of strokewidth is greater than radius, we don't handle that right now
3259 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3260 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003261 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003262 }
3263 }
3264
Brian Salomon8a97f562019-04-18 14:07:27 -04003265 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003266 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003267 std::swap(xRadius, yRadius);
3268 std::swap(scaledStroke.fX, scaledStroke.fY);
3269 }
3270
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003271 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3272 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3273 // patch will have fractional coverage. This only matters when the interior is actually filled.
3274 // We could consider falling back to rect rendering here, since a tiny radius is
3275 // indistinguishable from a square corner.
3276 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003277 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003278 }
3279
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003280 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003281 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3282 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003283}
3284
Herb Derbyc76d4092020-10-07 16:46:15 -04003285GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3286 GrPaint&& paint,
3287 const SkMatrix& viewMatrix,
3288 const SkRRect& rrect,
3289 const SkStrokeRec& stroke,
3290 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003291 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003292 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003293 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003294 }
3295
3296 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003297 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003298 }
3299
Greg Daniel2655ede2019-04-10 00:49:28 +00003300 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003301}
joshualitt3e708c52015-04-30 13:49:27 -07003302
bsalomon4b4a7cc2016-07-08 04:42:54 -07003303///////////////////////////////////////////////////////////////////////////////
3304
Herb Derbyc76d4092020-10-07 16:46:15 -04003305GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3306 GrPaint&& paint,
3307 const SkMatrix& viewMatrix,
3308 const SkRect& oval,
3309 const GrStyle& style,
3310 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003311 SkScalar width = oval.width();
3312 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3313 circle_stays_circle(viewMatrix));
3314
3315 auto r = width / 2.f;
3316 SkPoint center = { oval.centerX(), oval.centerY() };
3317 if (style.hasNonDashPathEffect()) {
3318 return nullptr;
3319 } else if (style.isDashed()) {
3320 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3321 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3322 return nullptr;
3323 }
3324 auto onInterval = style.dashIntervals()[0];
3325 auto offInterval = style.dashIntervals()[1];
3326 if (offInterval == 0) {
3327 GrStyle strokeStyle(style.strokeRec(), nullptr);
3328 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3329 strokeStyle, shaderCaps);
3330 } else if (onInterval == 0) {
3331 // There is nothing to draw but we have no way to indicate that here.
3332 return nullptr;
3333 }
3334 auto angularOnInterval = onInterval / r;
3335 auto angularOffInterval = offInterval / r;
3336 auto phaseAngle = style.dashPhase() / r;
3337 // Currently this function doesn't accept ovals with different start angles, though
3338 // it could.
3339 static const SkScalar kStartAngle = 0.f;
3340 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3341 style.strokeRec().getWidth(), kStartAngle,
3342 angularOnInterval, angularOffInterval, phaseAngle);
3343 }
3344 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3345}
3346
Herb Derbyc76d4092020-10-07 16:46:15 -04003347GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3348 GrPaint&& paint,
3349 const SkMatrix& viewMatrix,
3350 const SkRect& oval,
3351 const GrStyle& style,
3352 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003353 if (style.pathEffect()) {
3354 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003355 }
3356
Stan Ilieveb868aa2017-02-21 11:06:16 -05003357 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003358 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003359 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003360 }
3361
Stan Ilieveb868aa2017-02-21 11:06:16 -05003362 // Otherwise, if we have shader derivative support, render as device-independent
3363 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003364 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3365 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3366 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3367 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3368 // Check for near-degenerate matrix
3369 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003370 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003371 style.strokeRec());
3372 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003373 }
3374
bsalomon4b4a7cc2016-07-08 04:42:54 -07003375 return nullptr;
3376}
3377
3378///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003379
Herb Derbyc76d4092020-10-07 16:46:15 -04003380GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3381 GrPaint&& paint,
3382 const SkMatrix& viewMatrix,
3383 const SkRect& oval, SkScalar startAngle,
3384 SkScalar sweepAngle, bool useCenter,
3385 const GrStyle& style,
3386 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003387 SkASSERT(!oval.isEmpty());
3388 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003389 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003390 if (SkScalarAbs(sweepAngle) >= 360.f) {
3391 return nullptr;
3392 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003393 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3394 return nullptr;
3395 }
3396 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003397 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3398 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003399 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003400 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003401}
3402
3403///////////////////////////////////////////////////////////////////////////////
3404
Hal Canary6f6961e2017-01-31 13:50:44 -05003405#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003406
Brian Salomon05441c42017-05-15 16:45:49 -04003407GR_DRAW_OP_TEST_DEFINE(CircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003408 if (numSamples > 1) {
3409 return nullptr;
3410 }
3411
bsalomon4f3a0ca2016-08-22 13:14:26 -07003412 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003413 SkScalar rotate = random->nextSScalar1() * 360.f;
3414 SkScalar translateX = random->nextSScalar1() * 1000.f;
3415 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003416 SkScalar scale;
3417 do {
3418 scale = random->nextSScalar1() * 100.f;
3419 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003420 SkMatrix viewMatrix;
3421 viewMatrix.setRotate(rotate);
3422 viewMatrix.postTranslate(translateX, translateY);
3423 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003424 SkRect circle = GrTest::TestSquare(random);
3425 SkPoint center = {circle.centerX(), circle.centerY()};
3426 SkScalar radius = circle.width() / 2.f;
3427 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003428 CircleOp::ArcParams arcParamsTmp;
3429 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003430 if (random->nextBool()) {
3431 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003432 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3433 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003434 arcParams = &arcParamsTmp;
3435 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003436 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3437 center, radius,
3438 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003439 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003440 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003441 }
Mike Klein16885072018-12-11 09:54:31 -05003442 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003443 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003444}
3445
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003446GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003447 if (numSamples > 1) {
3448 return nullptr;
3449 }
3450
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003451 SkScalar rotate = random->nextSScalar1() * 360.f;
3452 SkScalar translateX = random->nextSScalar1() * 1000.f;
3453 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003454 SkScalar scale;
3455 do {
3456 scale = random->nextSScalar1() * 100.f;
3457 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003458 SkMatrix viewMatrix;
3459 viewMatrix.setRotate(rotate);
3460 viewMatrix.postTranslate(translateX, translateY);
3461 viewMatrix.postScale(scale, scale);
3462 SkRect circle = GrTest::TestSquare(random);
3463 SkPoint center = {circle.centerX(), circle.centerY()};
3464 SkScalar radius = circle.width() / 2.f;
3465 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3466 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3467 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3468 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3469 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003470 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3471 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003472 startAngle, onAngle, offAngle, phase);
3473}
3474
Brian Salomon05441c42017-05-15 16:45:49 -04003475GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003476 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003477 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003478 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003479 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003480}
3481
Brian Salomon05441c42017-05-15 16:45:49 -04003482GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003483 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003484 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003485 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003486 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003487}
3488
Jim Van Verth64b85892019-06-17 12:01:46 -04003489GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3490 do {
3491 SkScalar rotate = random->nextSScalar1() * 360.f;
3492 SkScalar translateX = random->nextSScalar1() * 1000.f;
3493 SkScalar translateY = random->nextSScalar1() * 1000.f;
3494 SkScalar scale;
3495 do {
3496 scale = random->nextSScalar1() * 100.f;
3497 } while (scale == 0);
3498 SkMatrix viewMatrix;
3499 viewMatrix.setRotate(rotate);
3500 viewMatrix.postTranslate(translateX, translateY);
3501 viewMatrix.postScale(scale, scale);
3502 SkRect rect = GrTest::TestRect(random);
3503 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3504 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3505 if (rrect.isOval()) {
3506 continue;
3507 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003508 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003509 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3510 GrTest::TestStrokeRec(random), nullptr);
3511 if (op) {
3512 return op;
3513 }
3514 assert_alive(paint);
3515 } while (true);
3516}
3517
Brian Salomon05441c42017-05-15 16:45:49 -04003518GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003519 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003520 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003521 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003522 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003523}
3524
3525#endif