blob: a43a4529f3816f009517f82fab994c00de30dba0 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#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 Osmanf04fb3c2018-11-12 15:34:00 -0500144 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "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;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700147 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
148 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400149 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400150 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700151 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
152 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400153 if (cgp.fInUnionPlane.isInitialized()) {
154 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400155 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700156 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
157 }
Brian Salomon45c92202018-04-10 10:53:58 -0400158 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400159 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400160 fragBuilder->codeAppend("float4 roundCapCenters;");
161 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
162 varyingHandler->addVarying("capRadius", &capRadius,
163 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
164 // This is the cap radius in normalized space where the outer radius is 1 and
165 // circledEdge.w is the normalized inner radius.
166 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500167 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400168 }
joshualittabb52a12015-01-13 15:02:10 -0800169
joshualittb8c241a2015-05-19 08:23:30 -0700170 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500171 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500172 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800173
joshualittabb52a12015-01-13 15:02:10 -0800174 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400175 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
176 WriteLocalCoord(vertBuilder,
177 uniformHandler,
178 *args.fShaderCaps,
179 gpArgs,
180 cgp.fInPosition.asShaderVar(),
181 cgp.fLocalMatrix,
182 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800183
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400184 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500185 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000186 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800187 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500188 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500189 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000190 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800191 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000192 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000193
Brian Salomon92be2f72018-06-19 14:33:47 -0400194 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500195 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000196 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
197 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400198 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500199 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000200 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
201 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700202 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400203 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500204 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000205 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
206 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700207 }
208 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400209 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400210 // We compute coverage of the round caps as circles at the butt caps produced
211 // by the clip planes. The inverse of the clip planes is applied so that there
212 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400213 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500214 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
215 " roundCapCenters.xy)));"
216 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
217 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400218 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400219 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
220 capRadius.fsIn(), capRadius.fsIn());
221 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700222 }
John Stiles4d7ac492021-03-09 20:16:43 -0500223 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224 }
225
Michael Ludwig553db622020-06-19 10:47:30 -0400226 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
227 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 };
229
Brian Salomon289e3d82016-12-14 15:52:56 -0500230 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400231
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500232 Attribute fInPosition;
233 Attribute fInColor;
234 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400235 // Optional attributes.
236 Attribute fInClipPlane;
237 Attribute fInIsectPlane;
238 Attribute fInUnionPlane;
239 Attribute fInRoundCapCenters;
240
Brian Salomon289e3d82016-12-14 15:52:56 -0500241 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400242 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243
John Stiles7571f9e2020-09-02 22:42:33 -0400244 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245};
246
bsalomoncdaa97b2016-03-08 08:30:14 -0800247GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248
Hal Canary6f6961e2017-01-31 13:50:44 -0500249#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500250GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400251 bool stroke = d->fRandom->nextBool();
252 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500253 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400254 bool clipPlane = d->fRandom->nextBool();
255 bool isectPlane = d->fRandom->nextBool();
256 bool unionPlane = d->fRandom->nextBool();
257 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500258 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
259 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000260}
Hal Canary6f6961e2017-01-31 13:50:44 -0500261#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000262
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400263class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
264public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500265 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
266 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600267 return arena->make([&](void* ptr) {
268 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
269 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400270 }
271
272 ~ButtCapDashedCircleGeometryProcessor() override {}
273
274 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
275
Brian Salomon13b28732021-08-06 15:33:58 -0400276 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400277 b->addBits(ProgramImpl::kMatrixKeyBits,
278 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
279 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400280 }
281
Brian Salomonf95940b2021-08-09 15:56:24 -0400282 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400283 return std::make_unique<Impl>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400284 }
285
286private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500287 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
288 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
289 , fLocalMatrix(localMatrix) {
290 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
291 fInColor = MakeColorAttribute("inColor", wideColor);
292 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
293 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 this->setVertexAttributes(&fInPosition, 4);
295 }
296
Brian Salomonbab2d112021-08-11 09:59:56 -0400297 class Impl : public ProgramImpl {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400299 void setData(const GrGLSLProgramDataManager& pdman,
300 const GrShaderCaps& shaderCaps,
301 const GrGeometryProcessor& geomProc) override {
302 SetTransform(pdman,
303 shaderCaps,
304 fLocalMatrixUniform,
305 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
306 &fLocalMatrix);
307 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400308
Brian Salomonbab2d112021-08-11 09:59:56 -0400309 private:
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400310 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
311 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400312 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400313 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
314 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
315 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
316 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
317
318 // emit attributes
319 varyingHandler->emitAttributes(bcscgp);
320 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500321 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400322
323 fragBuilder->codeAppend("float4 dashParams;");
324 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
327 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
328 varyingHandler->addVarying("wrapDashes", &wrapDashes,
329 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
330 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
331 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500333 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400334 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
335 // x = length of on interval, y = length of on + off.
336 // There are two other parameters in dashParams.zw:
337 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
338 // Each interval has a "corresponding" dash which may be shifted partially or
339 // fully out of its interval by the phase. So there may be up to two "visual"
340 // dashes in an interval.
341 // When computing coverage in an interval we look at three dashes. These are the
342 // "corresponding" dashes from the current, previous, and next intervals. Any of these
343 // may be phase shifted into our interval or even when phase=0 they may be within half a
344 // pixel distance of a pixel center in the interval.
345 // When in the first interval we need to check the dash from the last interval. And
346 // similarly when in the last interval we need to check the dash from the first
347 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
348 // We compute the dash begin/end angles in the vertex shader and apply them in the
349 // fragment shader when we detect we're in the first/last interval.
350 vertBuilder->codeAppend(R"(
351 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
352 // to the fragment shader as a varying.
353 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 // We can happen to be perfectly divisible.
356 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 }
359 // Let 'l' be the last interval before reaching 2 pi.
360 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
361 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
362 // interval.
363 half offset = 0;
364 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500365 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400366 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500367 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400368 }
369 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
370 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
371 // min.
372 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
373
374 // Based on the phase determine whether the -1st, 0th, or 1st interval's
375 // "corresponding" dash appears in the 0th interval and is closest to l.
376 offset = 0;
377 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500378 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400379 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 }
382 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
383 wrapDashes.w = wrapDashes.z + dashParams.x;
384 // The start of the dash we're considering may be clipped by the start of the
385 // circle.
386 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
387 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500388 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
390 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
391 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
392
393 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500394 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500396 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400397 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
398
399 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400400 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
401 WriteLocalCoord(vertBuilder,
402 uniformHandler,
403 *args.fShaderCaps,
404 gpArgs,
405 bcscgp.fInPosition.asShaderVar(),
406 bcscgp.fLocalMatrix,
407 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400409 GrShaderVar fnArgs[] = {
410 GrShaderVar("angleToEdge", kFloat_GrSLType),
411 GrShaderVar("diameter", kFloat_GrSLType),
412 };
John Stiles6b58a332020-10-26 17:53:06 -0400413 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
414 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400415 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 float linearDist;
417 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
418 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400419 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400420 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 fragBuilder->codeAppend(R"(
422 float d = length(circleEdge.xy) * circleEdge.z;
423
424 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500427 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400428 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400429 edgeAlpha *= innerAlpha;
430
Ethan Nicholase1f55022019-02-05 17:17:40 -0500431 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 angleFromStart = mod(angleFromStart, 6.28318530718);
433 float x = mod(angleFromStart, dashParams.y);
434 // Convert the radial distance from center to pixel into a diameter.
435 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
437 half(dashParams.w));
438 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
439 half(dashParams.y) + half(dashParams.x) -
440 half(dashParams.w));
441 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
442 half(-dashParams.y) + half(dashParams.x) -
443 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400444 half dashAlpha = 0;
445 )");
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.y = min(currDash.y, lastIntervalLength);
450 if (nextDash.x >= lastIntervalLength) {
451 // The next dash is outside the 0..2pi range, throw it away
452 nextDash.xy = half2(1000);
453 } else {
454 // Clip the end of the next dash to the end of the circle
455 nextDash.y = min(nextDash.y, lastIntervalLength);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
460 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400462 currDash.x = max(currDash.x, 0);
463 if (prevDash.y <= 0) {
464 // The previous dash is outside the 0..2pi range, throw it away
465 prevDash.xy = half2(1000);
466 } else {
467 // Clip the start previous dash to the start of the circle
468 prevDash.x = max(prevDash.x, 0);
469 }
470 }
471 )", fnName.c_str(), fnName.c_str());
472 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500473 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
474 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
475 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 dashAlpha = min(dashAlpha, 1);
477 edgeAlpha *= dashAlpha;
478 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
479 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500480 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400481 }
482
Michael Ludwig553db622020-06-19 10:47:30 -0400483 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
484 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 };
486
487 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500488 Attribute fInPosition;
489 Attribute fInColor;
490 Attribute fInCircleEdge;
491 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400492
493 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
494
John Stiles7571f9e2020-09-02 22:42:33 -0400495 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400496};
497
498#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500499GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500500 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500502 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400503}
504#endif
505
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000506///////////////////////////////////////////////////////////////////////////////
507
508/**
509 * 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 +0000510 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
511 * in both x and y directions.
512 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000513 * 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 +0000514 */
515
bsalomoncdaa97b2016-03-08 08:30:14 -0800516class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000517public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500518 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
519 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600520 return arena->make([&](void* ptr) {
521 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
522 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500523 }
524
525 ~EllipseGeometryProcessor() override {}
526
527 const char* name() const override { return "EllipseGeometryProcessor"; }
528
Brian Salomon13b28732021-08-06 15:33:58 -0400529 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400530 b->addBool(fStroke, "stroked");
531 b->addBits(ProgramImpl::kMatrixKeyBits,
532 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
533 "localMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500534 }
535
Brian Salomonf95940b2021-08-09 15:56:24 -0400536 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400537 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500538 }
539
540private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000541 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400542 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500543 : INHERITED(kEllipseGeometryProcessor_ClassID)
544 , fLocalMatrix(localMatrix)
545 , fStroke(stroke)
546 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500547 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500548 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400549 if (useScale) {
550 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
551 } else {
552 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
553 }
554 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500555 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000556 }
557
Brian Salomonbab2d112021-08-11 09:59:56 -0400558 class Impl : public ProgramImpl {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400560 void setData(const GrGLSLProgramDataManager& pdman,
561 const GrShaderCaps& shaderCaps,
562 const GrGeometryProcessor& geomProc) override {
563 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
564 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
565 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566
Brian Salomonbab2d112021-08-11 09:59:56 -0400567 private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500568 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400569 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800570 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800571 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800572 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000573
joshualittabb52a12015-01-13 15:02:10 -0800574 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800575 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800576
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400577 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
578 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800579 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800580 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000582
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400583 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800584 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500585 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800586
Chris Dalton60283612018-02-14 13:38:14 -0700587 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700588 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500589 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500590 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800591
joshualittabb52a12015-01-13 15:02:10 -0800592 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400593 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
594 WriteLocalCoord(vertBuilder,
595 uniformHandler,
596 *args.fShaderCaps,
597 gpArgs,
598 egp.fInPosition.asShaderVar(),
599 egp.fLocalMatrix,
600 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800601
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400602 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
603 // to compute both the edges because we need two separate test equations for
604 // the single offset.
605 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
606 // the distance by the gradient, non-uniformly scaled by the inverse of the
607 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800608
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400609 // On medium precision devices, we scale the denominator of the distance equation
610 // before taking the inverse square root to minimize the chance that we're dividing
611 // by zero, then we scale the result back.
612
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000613 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400614 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400615 if (egp.fStroke) {
616 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
617 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400618 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
619 if (egp.fUseScale) {
620 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
621 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
622 } else {
623 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
624 }
625 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700626
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000627 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400628 if (args.fShaderCaps->floatIs32Bits()) {
629 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
630 } else {
631 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
632 }
633 if (egp.fUseScale) {
634 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
635 ellipseOffsets.fsIn());
636 } else {
637 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
638 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000639 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000640
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000641 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800642 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400643 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800644 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400645 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400646 if (egp.fUseScale) {
647 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
648 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
649 } else {
650 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
651 }
652 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
653 if (!args.fShaderCaps->floatIs32Bits()) {
654 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
655 }
656 if (egp.fUseScale) {
657 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
658 ellipseOffsets.fsIn());
659 } else {
660 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
661 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000662 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 }
664
John Stiles4d7ac492021-03-09 20:16:43 -0500665 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666 }
667
Brian Salomonf95940b2021-08-09 15:56:24 -0400668 using INHERITED = ProgramImpl;
Michael Ludwig553db622020-06-19 10:47:30 -0400669
670 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
671 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000672 };
673
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500674 Attribute fInPosition;
675 Attribute fInColor;
676 Attribute fInEllipseOffset;
677 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400678
joshualitte3ababe2015-05-15 07:56:07 -0700679 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400681 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000682
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400683 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000684
John Stiles7571f9e2020-09-02 22:42:33 -0400685 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000686};
687
bsalomoncdaa97b2016-03-08 08:30:14 -0800688GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Hal Canary6f6961e2017-01-31 13:50:44 -0500690#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500691GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400692 bool stroke = d->fRandom->nextBool();
693 bool wideColor = d->fRandom->nextBool();
694 bool useScale = d->fRandom->nextBool();
695 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
696 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000697}
Hal Canary6f6961e2017-01-31 13:50:44 -0500698#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699
700///////////////////////////////////////////////////////////////////////////////
701
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000702/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000703 * 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 +0000704 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
705 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
706 * using differentials.
707 *
708 * The result is device-independent and can be used with any affine matrix.
709 */
710
bsalomoncdaa97b2016-03-08 08:30:14 -0800711enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000712
bsalomoncdaa97b2016-03-08 08:30:14 -0800713class DIEllipseGeometryProcessor : public GrGeometryProcessor {
714public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500715 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
716 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600717 return arena->make([&](void* ptr) {
718 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
719 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500720 }
721
722 ~DIEllipseGeometryProcessor() override {}
723
724 const char* name() const override { return "DIEllipseGeometryProcessor"; }
725
Brian Salomon13b28732021-08-06 15:33:58 -0400726 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400727 b->addBits(2, static_cast<uint32_t>(fStyle), "style");
728 b->addBits(ProgramImpl::kMatrixKeyBits,
729 ProgramImpl::ComputeMatrixKey(caps, fViewMatrix),
730 "viewMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500731 }
732
Brian Salomonf95940b2021-08-09 15:56:24 -0400733 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400734 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500735 }
736
737private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000738 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400739 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400740 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400741 , fViewMatrix(viewMatrix)
742 , fUseScale(useScale)
743 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500744 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500745 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400746 if (useScale) {
747 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
748 kFloat3_GrSLType};
749 } else {
750 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
751 kFloat2_GrSLType};
752 }
753 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500754 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000755 }
756
Brian Salomonbab2d112021-08-11 09:59:56 -0400757 class Impl : public ProgramImpl {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000758 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400759 void setData(const GrGLSLProgramDataManager& pdman,
760 const GrShaderCaps& shaderCaps,
761 const GrGeometryProcessor& geomProc) override {
762 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763
Brian Salomonbab2d112021-08-11 09:59:56 -0400764 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
765 }
766
767 private:
joshualitt465283c2015-09-11 08:19:35 -0700768 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400769 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800770 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800771 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800772 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000773
joshualittabb52a12015-01-13 15:02:10 -0800774 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800775 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800776
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400777 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
778 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800779 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500780 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700781
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400782 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800783 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500784 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800785
Chris Dalton60283612018-02-14 13:38:14 -0700786 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500787 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500788 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800789
joshualittabb52a12015-01-13 15:02:10 -0800790 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400791 WriteOutputPosition(vertBuilder,
792 uniformHandler,
793 *args.fShaderCaps,
794 gpArgs,
795 diegp.fInPosition.name(),
796 diegp.fViewMatrix,
797 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400798 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800799
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000800 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400801 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
802 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
803 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
804 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500805 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400806 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
807 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500808 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 if (diegp.fUseScale) {
810 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
811 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000812
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400813 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000814 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400815 if (args.fShaderCaps->floatIs32Bits()) {
816 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
817 } else {
818 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
819 }
820 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
821 if (diegp.fUseScale) {
822 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
823 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800824 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000825 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000826 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
827 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000828 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000829 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000830 }
831
832 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800833 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800834 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
835 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400836 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
837 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500838 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400839 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
840 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500841 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400842 if (diegp.fUseScale) {
843 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
844 }
845 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
846 if (!args.fShaderCaps->floatIs32Bits()) {
847 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
848 }
849 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
850 if (diegp.fUseScale) {
851 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
852 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000853 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000854 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000855
John Stiles4d7ac492021-03-09 20:16:43 -0500856 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000857 }
858
Brian Salomonbab2d112021-08-11 09:59:56 -0400859 SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
joshualitt5559ca22015-05-21 15:50:36 -0700860 UniformHandle fViewMatrixUniform;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000861 };
862
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500863 Attribute fInPosition;
864 Attribute fInColor;
865 Attribute fInEllipseOffsets0;
866 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400867
Brian Salomon289e3d82016-12-14 15:52:56 -0500868 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400869 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500870 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000871
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400872 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000873
John Stiles7571f9e2020-09-02 22:42:33 -0400874 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000875};
876
bsalomoncdaa97b2016-03-08 08:30:14 -0800877GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000878
Hal Canary6f6961e2017-01-31 13:50:44 -0500879#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500880GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400881 bool wideColor = d->fRandom->nextBool();
882 bool useScale = d->fRandom->nextBool();
883 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
884 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
885 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886}
Hal Canary6f6961e2017-01-31 13:50:44 -0500887#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888
889///////////////////////////////////////////////////////////////////////////////
890
jvanverth6ca48822016-10-07 06:57:32 -0700891// We have two possible cases for geometry for a circle:
892
893// In the case of a normal fill, we draw geometry for the circle as an octagon.
894static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500895 // enter the octagon
896 // clang-format off
897 0, 1, 8, 1, 2, 8,
898 2, 3, 8, 3, 4, 8,
899 4, 5, 8, 5, 6, 8,
900 6, 7, 8, 7, 0, 8
901 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700902};
903
904// For stroked circles, we use two nested octagons.
905static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500906 // enter the octagon
907 // clang-format off
908 0, 1, 9, 0, 9, 8,
909 1, 2, 10, 1, 10, 9,
910 2, 3, 11, 2, 11, 10,
911 3, 4, 12, 3, 12, 11,
912 4, 5, 13, 4, 13, 12,
913 5, 6, 14, 5, 14, 13,
914 6, 7, 15, 6, 15, 14,
915 7, 0, 8, 7, 8, 15,
916 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700917};
918
Brian Osman9d958b52018-11-13 12:46:56 -0500919// Normalized geometry for octagons that circumscribe and lie on a circle:
920
921static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
922static constexpr SkPoint kOctagonOuter[] = {
923 SkPoint::Make(-kOctOffset, -1),
924 SkPoint::Make( kOctOffset, -1),
925 SkPoint::Make( 1, -kOctOffset),
926 SkPoint::Make( 1, kOctOffset),
927 SkPoint::Make( kOctOffset, 1),
928 SkPoint::Make(-kOctOffset, 1),
929 SkPoint::Make(-1, kOctOffset),
930 SkPoint::Make(-1, -kOctOffset),
931};
932
933// cosine and sine of pi/8
934static constexpr SkScalar kCosPi8 = 0.923579533f;
935static constexpr SkScalar kSinPi8 = 0.382683432f;
936static constexpr SkPoint kOctagonInner[] = {
937 SkPoint::Make(-kSinPi8, -kCosPi8),
938 SkPoint::Make( kSinPi8, -kCosPi8),
939 SkPoint::Make( kCosPi8, -kSinPi8),
940 SkPoint::Make( kCosPi8, kSinPi8),
941 SkPoint::Make( kSinPi8, kCosPi8),
942 SkPoint::Make(-kSinPi8, kCosPi8),
943 SkPoint::Make(-kCosPi8, kSinPi8),
944 SkPoint::Make(-kCosPi8, -kSinPi8),
945};
Brian Salomon289e3d82016-12-14 15:52:56 -0500946
jvanverth6ca48822016-10-07 06:57:32 -0700947static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
948static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
949static const int kVertsPerStrokeCircle = 16;
950static const int kVertsPerFillCircle = 9;
951
952static int circle_type_to_vert_count(bool stroked) {
953 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
954}
955
956static int circle_type_to_index_count(bool stroked) {
957 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
958}
959
960static const uint16_t* circle_type_to_indices(bool stroked) {
961 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
962}
963
964///////////////////////////////////////////////////////////////////////////////
965
Brian Salomon05441c42017-05-15 16:45:49 -0400966class CircleOp final : public GrMeshDrawOp {
967private:
968 using Helper = GrSimpleMeshDrawOpHelper;
969
joshualitt76e7fb62015-02-11 08:52:27 -0800970public:
Brian Salomon25a88092016-12-01 09:36:50 -0500971 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700972
bsalomon4f3a0ca2016-08-22 13:14:26 -0700973 /** Optional extra params to render a partial arc rather than a full circle. */
974 struct ArcParams {
975 SkScalar fStartAngleRadians;
976 SkScalar fSweepAngleRadians;
977 bool fUseCenter;
978 };
Brian Salomon05441c42017-05-15 16:45:49 -0400979
Herb Derbyc76d4092020-10-07 16:46:15 -0400980 static GrOp::Owner Make(GrRecordingContext* context,
981 GrPaint&& paint,
982 const SkMatrix& viewMatrix,
983 SkPoint center,
984 SkScalar radius,
985 const GrStyle& style,
986 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700987 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700988 if (style.hasPathEffect()) {
989 return nullptr;
990 }
Brian Salomon05441c42017-05-15 16:45:49 -0400991 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700992 SkStrokeRec::Style recStyle = stroke.getStyle();
993 if (arcParams) {
994 // Arc support depends on the style.
995 switch (recStyle) {
996 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500997 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 return nullptr;
999 case SkStrokeRec::kFill_Style:
1000 // This supports all fills.
1001 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001002 case SkStrokeRec::kStroke_Style:
1003 // Strokes that don't use the center point are supported with butt and round
1004 // caps.
1005 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1006 return nullptr;
1007 }
1008 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001009 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001010 // Hairline only supports butt cap. Round caps could be emulated by slightly
1011 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001012 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1013 return nullptr;
1014 }
1015 break;
1016 }
1017 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001018 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1019 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001020 }
1021
Herb Derbyc76d4092020-10-07 16:46:15 -04001022 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001023 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1024 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001025 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001026 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001027 const SkStrokeRec& stroke = style.strokeRec();
1028 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001029
Brian Salomon45c92202018-04-10 10:53:58 -04001030 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001031
bsalomon4b4a7cc2016-07-08 04:42:54 -07001032 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001033 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001034 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001035
Brian Salomon289e3d82016-12-14 15:52:56 -05001036 bool isStrokeOnly =
1037 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001038 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001039
jvanverth6ca48822016-10-07 06:57:32 -07001040 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001041 SkScalar outerRadius = radius;
1042 SkScalar halfWidth = 0;
1043 if (hasStroke) {
1044 if (SkScalarNearlyZero(strokeWidth)) {
1045 halfWidth = SK_ScalarHalf;
1046 } else {
1047 halfWidth = SkScalarHalf(strokeWidth);
1048 }
1049
1050 outerRadius += halfWidth;
1051 if (isStrokeOnly) {
1052 innerRadius = radius - halfWidth;
1053 }
1054 }
1055
1056 // The radii are outset for two reasons. First, it allows the shader to simply perform
1057 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1058 // Second, the outer radius is used to compute the verts of the bounding box that is
1059 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001060 outerRadius += SK_ScalarHalf;
1061 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001062 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001063 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001064
bsalomon4f3a0ca2016-08-22 13:14:26 -07001065 // This makes every point fully inside the intersection plane.
1066 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1067 // This makes every point fully outside the union plane.
1068 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001069 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001070 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1071 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001072 if (arcParams) {
1073 // The shader operates in a space where the circle is translated to be centered at the
1074 // origin. Here we compute points on the unit circle at the starting and ending angles.
1075 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001076 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1077 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001079 stopPoint.fY = SkScalarSin(endAngle);
1080 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001081
1082 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1083 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1084 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1085 startPoint.normalize();
1086 stopPoint.normalize();
1087
Brian Salomon3517aa72019-12-11 08:16:22 -05001088 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1089 // should orient the clip planes for arcs.
1090 SkASSERT(viewMatrix.isSimilarity());
1091 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1092 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1093 if (upperLeftDet < 0) {
1094 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001095 }
1096
Brian Salomon45c92202018-04-10 10:53:58 -04001097 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1098 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1099 SkPoint roundCaps[2];
1100 if (fRoundCaps) {
1101 // Compute the cap center points in the normalized space.
1102 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1103 roundCaps[0] = startPoint * midRadius;
1104 roundCaps[1] = stopPoint * midRadius;
1105 } else {
1106 roundCaps[0] = kUnusedRoundCaps[0];
1107 roundCaps[1] = kUnusedRoundCaps[1];
1108 }
1109
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001111 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1112 // center of the butts.
1113 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001114 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001115 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001116 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001117 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1118 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1119 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001120 if (useCenter) {
1121 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1122 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001123 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1124 if (arcParams->fSweepAngleRadians < 0) {
1125 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001127 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001128 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001129 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001130 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001131 color,
1132 innerRadius,
1133 outerRadius,
1134 {norm0.fX, norm0.fY, 0.5f},
1135 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1136 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001137 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001138 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001139 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001140 fClipPlaneIsect = false;
1141 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001143 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001144 color,
1145 innerRadius,
1146 outerRadius,
1147 {norm0.fX, norm0.fY, 0.5f},
1148 {norm1.fX, norm1.fY, 0.5f},
1149 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001150 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001151 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001152 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001153 fClipPlaneIsect = true;
1154 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001155 }
1156 } else {
1157 // We clip to a secant of the original circle.
1158 startPoint.scale(radius);
1159 stopPoint.scale(radius);
1160 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1161 norm.normalize();
1162 if (arcParams->fSweepAngleRadians > 0) {
1163 norm.negate();
1164 }
1165 SkScalar d = -norm.dot(startPoint) + 0.5f;
1166
Brian Salomon05441c42017-05-15 16:45:49 -04001167 fCircles.emplace_back(
1168 Circle{color,
1169 innerRadius,
1170 outerRadius,
1171 {norm.fX, norm.fY, d},
1172 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1173 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001174 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001175 devBounds,
1176 stroked});
1177 fClipPlane = true;
1178 fClipPlaneIsect = false;
1179 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001180 }
1181 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001182 fCircles.emplace_back(
1183 Circle{color,
1184 innerRadius,
1185 outerRadius,
1186 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1187 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1188 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001189 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001190 devBounds,
1191 stroked});
1192 fClipPlane = false;
1193 fClipPlaneIsect = false;
1194 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 }
bsalomon88cf17d2016-07-08 06:40:56 -07001196 // Use the original radius and stroke radius for the bounds so that it does not include the
1197 // AA bloat.
1198 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001199 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001200 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001201 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001202 fVertCount = circle_type_to_vert_count(stroked);
1203 fIndexCount = circle_type_to_index_count(stroked);
1204 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001205 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001206
Brian Salomon289e3d82016-12-14 15:52:56 -05001207 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001208
Robert Phillips294723d2021-06-17 09:23:58 -04001209 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001210 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001211 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001212 } else {
1213 fHelper.visitProxies(func);
1214 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001215 }
1216
Chris Dalton57ab06c2021-04-22 12:57:28 -06001217 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1218 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001219 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001220 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001221 GrProcessorAnalysisCoverage::kSingleChannel, color,
1222 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001223 }
1224
1225 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1226
bsalomone46f9fe2015-08-18 06:05:14 -07001227private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001228 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001229
Robert Phillips4133dc42020-03-11 15:55:55 -04001230 void onCreateProgramInfo(const GrCaps* caps,
1231 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001232 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001233 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001234 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001235 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001236 GrXferBarrierFlags renderPassXferBarriers,
1237 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001238 SkASSERT(!usesMSAASurface);
1239
bsalomoncdaa97b2016-03-08 08:30:14 -08001240 SkMatrix localMatrix;
1241 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001243 }
1244
Robert Phillips4490d922020-03-03 14:50:59 -05001245 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001246 fClipPlaneIsect, fClipPlaneUnion,
1247 fRoundCaps, fWideColor,
1248 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001249
Brian Salomon8afde5f2020-04-01 16:22:00 -04001250 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001251 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001252 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001253 }
1254
Robert Phillips71143952021-06-17 14:55:07 -04001255 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001256 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001257 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001258 if (!fProgramInfo) {
1259 return;
1260 }
1261 }
1262
Brian Salomon12d22642019-01-29 14:38:50 -05001263 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001264 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001265 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001266 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001267 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001268 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001269 return;
1270 }
1271
Brian Salomon12d22642019-01-29 14:38:50 -05001272 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001273 int firstIndex = 0;
1274 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1275 if (!indices) {
1276 SkDebugf("Could not allocate indices\n");
1277 return;
1278 }
1279
1280 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001281 for (const auto& circle : fCircles) {
1282 SkScalar innerRadius = circle.fInnerRadius;
1283 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001284 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001285 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001286
joshualitt76e7fb62015-02-11 08:52:27 -08001287 // The inner radius in the vertex data must be specified in normalized space.
1288 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001289 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001290
1291 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001292 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001293
Brian Osman9a24fee2018-08-03 09:48:42 -04001294 SkVector geoClipPlane = { 0, 0 };
1295 SkScalar offsetClipDist = SK_Scalar1;
1296 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1297 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1298 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1299 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1300 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1301 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1302 // the AA can extend just past the center of the circle.
1303 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1304 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1305 SkAssertResult(geoClipPlane.normalize());
1306 offsetClipDist = 0.5f / halfWidth;
1307 }
1308
Brian Osman7d8f82b2018-11-08 10:24:09 -05001309 for (int i = 0; i < 8; ++i) {
1310 // This clips the normalized offset to the half-plane we computed above. Then we
1311 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001312 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001313 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001314 vertices.write(center + offset * halfWidth,
1315 color,
1316 offset,
1317 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001318 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001319 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001320 }
1321 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001322 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001323 }
1324 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001325 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001326 }
1327 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001328 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001329 }
Brian Salomon45c92202018-04-10 10:53:58 -04001330 }
jvanverth6ca48822016-10-07 06:57:32 -07001331
Brian Salomon05441c42017-05-15 16:45:49 -04001332 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001333 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001334
Brian Osman7d8f82b2018-11-08 10:24:09 -05001335 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001336 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1337 color,
1338 kOctagonInner[i] * innerRadius,
1339 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001340 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001341 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001342 }
1343 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001344 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001345 }
1346 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001347 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 }
1349 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001350 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001351 }
Brian Salomon45c92202018-04-10 10:53:58 -04001352 }
jvanverth6ca48822016-10-07 06:57:32 -07001353 } else {
1354 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001356 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001357 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001358 }
jvanverth6ca48822016-10-07 06:57:32 -07001359 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001360 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001361 }
1362 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001364 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001365 if (fRoundCaps) {
1366 vertices.write(circle.fRoundCapCenters);
1367 }
jvanverth6ca48822016-10-07 06:57:32 -07001368 }
1369
Brian Salomon05441c42017-05-15 16:45:49 -04001370 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1371 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001372 for (int i = 0; i < primIndexCount; ++i) {
1373 *indices++ = primIndices[i] + currStartVertex;
1374 }
1375
Brian Salomon05441c42017-05-15 16:45:49 -04001376 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001377 }
jvanverth6ca48822016-10-07 06:57:32 -07001378
Robert Phillips4490d922020-03-03 14:50:59 -05001379 fMesh = target->allocMesh();
1380 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001381 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001382 }
1383
1384 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001385 if (!fProgramInfo || !fMesh) {
1386 return;
1387 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001388
Chris Dalton765ed362020-03-16 17:34:44 -06001389 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001390 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001391 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001392 }
1393
Herb Derbye25c3002020-10-27 15:57:27 -04001394 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001395 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001396
1397 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001398 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001399 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001400 }
1401
Brian Salomon05441c42017-05-15 16:45:49 -04001402 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001403 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001404 }
1405
Brian Salomon05441c42017-05-15 16:45:49 -04001406 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001407 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1408 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001409 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001410 }
1411
Brian Salomon289e3d82016-12-14 15:52:56 -05001412 // Because we've set up the ops that don't use the planes with noop values
1413 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001414 fClipPlane |= that->fClipPlane;
1415 fClipPlaneIsect |= that->fClipPlaneIsect;
1416 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001417 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001418 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001419
Brian Salomon05441c42017-05-15 16:45:49 -04001420 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001421 fVertCount += that->fVertCount;
1422 fIndexCount += that->fIndexCount;
1423 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001424 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001425 }
1426
John Stilesaf366522020-08-13 09:57:34 -04001427#if GR_TEST_UTILS
1428 SkString onDumpInfo() const override {
1429 SkString string;
1430 for (int i = 0; i < fCircles.count(); ++i) {
1431 string.appendf(
1432 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1433 "InnerRad: %.2f, OuterRad: %.2f\n",
1434 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1435 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1436 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1437 fCircles[i].fOuterRadius);
1438 }
1439 string += fHelper.dumpInfo();
1440 return string;
1441 }
1442#endif
1443
Brian Salomon05441c42017-05-15 16:45:49 -04001444 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001445 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001446 SkScalar fInnerRadius;
1447 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001448 SkScalar fClipPlane[3];
1449 SkScalar fIsectPlane[3];
1450 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001451 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001452 SkRect fDevBounds;
1453 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 };
1455
Brian Salomon289e3d82016-12-14 15:52:56 -05001456 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001457 Helper fHelper;
1458 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001459 int fVertCount;
1460 int fIndexCount;
1461 bool fAllFill;
1462 bool fClipPlane;
1463 bool fClipPlaneIsect;
1464 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001465 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001466 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001467
Chris Daltoneb694b72020-03-16 09:25:50 -06001468 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001469 GrProgramInfo* fProgramInfo = nullptr;
1470
John Stiles7571f9e2020-09-02 22:42:33 -04001471 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001472};
1473
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001474class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1475private:
1476 using Helper = GrSimpleMeshDrawOpHelper;
1477
1478public:
1479 DEFINE_OP_CLASS_ID
1480
Herb Derbyc76d4092020-10-07 16:46:15 -04001481 static GrOp::Owner Make(GrRecordingContext* context,
1482 GrPaint&& paint,
1483 const SkMatrix& viewMatrix,
1484 SkPoint center,
1485 SkScalar radius,
1486 SkScalar strokeWidth,
1487 SkScalar startAngle,
1488 SkScalar onAngle,
1489 SkScalar offAngle,
1490 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001491 SkASSERT(circle_stays_circle(viewMatrix));
1492 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001493 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1494 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001495 onAngle, offAngle, phaseAngle);
1496 }
1497
Herb Derbyc76d4092020-10-07 16:46:15 -04001498 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001499 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1500 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1501 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001502 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001503 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001504 SkASSERT(circle_stays_circle(viewMatrix));
1505 viewMatrix.mapPoints(&center, 1);
1506 radius = viewMatrix.mapRadius(radius);
1507 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1508
1509 // Determine the angle where the circle starts in device space and whether its orientation
1510 // has been reversed.
1511 SkVector start;
1512 bool reflection;
1513 if (!startAngle) {
1514 start = {1, 0};
1515 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001516 start.fY = SkScalarSin(startAngle);
1517 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001518 }
1519 viewMatrix.mapVectors(&start, 1);
1520 startAngle = SkScalarATan2(start.fY, start.fX);
1521 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1522 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1523
1524 auto totalAngle = onAngle + offAngle;
1525 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1526
1527 SkScalar halfWidth = 0;
1528 if (SkScalarNearlyZero(strokeWidth)) {
1529 halfWidth = SK_ScalarHalf;
1530 } else {
1531 halfWidth = SkScalarHalf(strokeWidth);
1532 }
1533
1534 SkScalar outerRadius = radius + halfWidth;
1535 SkScalar innerRadius = radius - halfWidth;
1536
1537 // The radii are outset for two reasons. First, it allows the shader to simply perform
1538 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1539 // Second, the outer radius is used to compute the verts of the bounding box that is
1540 // rendered and the outset ensures the box will cover all partially covered by the circle.
1541 outerRadius += SK_ScalarHalf;
1542 innerRadius -= SK_ScalarHalf;
1543 fViewMatrixIfUsingLocalCoords = viewMatrix;
1544
1545 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1546 center.fX + outerRadius, center.fY + outerRadius);
1547
1548 // We store whether there is a reflection as a negative total angle.
1549 if (reflection) {
1550 totalAngle = -totalAngle;
1551 }
1552 fCircles.push_back(Circle{
1553 color,
1554 outerRadius,
1555 innerRadius,
1556 onAngle,
1557 totalAngle,
1558 startAngle,
1559 phaseAngle,
1560 devBounds
1561 });
1562 // Use the original radius and stroke radius for the bounds so that it does not include the
1563 // AA bloat.
1564 radius += halfWidth;
1565 this->setBounds(
1566 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001567 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001568 fVertCount = circle_type_to_vert_count(true);
1569 fIndexCount = circle_type_to_index_count(true);
1570 }
1571
1572 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1573
Robert Phillips294723d2021-06-17 09:23:58 -04001574 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001575 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001576 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001577 } else {
1578 fHelper.visitProxies(func);
1579 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001580 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001581
Chris Dalton57ab06c2021-04-22 12:57:28 -06001582 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1583 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001584 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001585 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001586 GrProcessorAnalysisCoverage::kSingleChannel, color,
1587 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001588 }
1589
1590 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1591
1592private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001593 GrProgramInfo* programInfo() override { return fProgramInfo; }
1594
Robert Phillips4133dc42020-03-11 15:55:55 -04001595 void onCreateProgramInfo(const GrCaps* caps,
1596 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001597 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001598 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001599 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001600 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001601 GrXferBarrierFlags renderPassXferBarriers,
1602 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001603 SkASSERT(!usesMSAASurface);
1604
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001605 SkMatrix localMatrix;
1606 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001607 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001608 }
1609
1610 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001611 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001612 fWideColor,
1613 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001614
Brian Salomon8afde5f2020-04-01 16:22:00 -04001615 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001616 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001617 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001618 }
1619
Robert Phillips71143952021-06-17 14:55:07 -04001620 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001621 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001622 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001623 if (!fProgramInfo) {
1624 return;
1625 }
1626 }
1627
Brian Salomon12d22642019-01-29 14:38:50 -05001628 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001629 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001630 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001631 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001632 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001633 SkDebugf("Could not allocate vertices\n");
1634 return;
1635 }
1636
Brian Salomon12d22642019-01-29 14:38:50 -05001637 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001638 int firstIndex = 0;
1639 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1640 if (!indices) {
1641 SkDebugf("Could not allocate indices\n");
1642 return;
1643 }
1644
1645 int currStartVertex = 0;
1646 for (const auto& circle : fCircles) {
1647 // The inner radius in the vertex data must be specified in normalized space so that
1648 // length() can be called with smaller values to avoid precision issues with half
1649 // floats.
1650 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1651 const SkRect& bounds = circle.fDevBounds;
1652 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001653 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1654 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1655 };
1656 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001657 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001658 dashParams.totalAngle = -dashParams.totalAngle;
1659 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 }
1661
Brian Osmane3caf2d2018-11-21 13:48:36 -05001662 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001663
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001664 // The bounding geometry for the circle is composed of an outer bounding octagon and
1665 // an inner bounded octagon.
1666
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 // Compute the vertices of the outer octagon.
1668 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1669 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001670
1671 auto reflectY = [=](const SkPoint& p) {
1672 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001673 };
Brian Osman9d958b52018-11-13 12:46:56 -05001674
1675 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001676 vertices.write(center + kOctagonOuter[i] * halfWidth,
1677 color,
1678 reflectY(kOctagonOuter[i]),
1679 circle.fOuterRadius,
1680 normInnerRadius,
1681 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001682 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001683
1684 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001685 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001686 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1687 color,
1688 reflectY(kOctagonInner[i]) * normInnerRadius,
1689 circle.fOuterRadius,
1690 normInnerRadius,
1691 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001692 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001693
1694 const uint16_t* primIndices = circle_type_to_indices(true);
1695 const int primIndexCount = circle_type_to_index_count(true);
1696 for (int i = 0; i < primIndexCount; ++i) {
1697 *indices++ = primIndices[i] + currStartVertex;
1698 }
1699
1700 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001701 }
1702
Robert Phillips4490d922020-03-03 14:50:59 -05001703 fMesh = target->allocMesh();
1704 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001705 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001706 }
1707
1708 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001709 if (!fProgramInfo || !fMesh) {
1710 return;
1711 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001712
Chris Dalton765ed362020-03-16 17:34:44 -06001713 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001714 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001715 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001716 }
1717
Herb Derbye25c3002020-10-27 15:57:27 -04001718 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001719 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1720
1721 // can only represent 65535 unique vertices with 16-bit indices
1722 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001723 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001724 }
1725
1726 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001727 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001728 }
1729
1730 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001731 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1732 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001733 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001734 }
1735
1736 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001737 fVertCount += that->fVertCount;
1738 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001739 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001740 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 }
1742
John Stilesaf366522020-08-13 09:57:34 -04001743#if GR_TEST_UTILS
1744 SkString onDumpInfo() const override {
1745 SkString string;
1746 for (int i = 0; i < fCircles.count(); ++i) {
1747 string.appendf(
1748 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1749 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1750 "Phase: %.2f\n",
1751 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1752 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1753 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1754 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1755 fCircles[i].fPhaseAngle);
1756 }
1757 string += fHelper.dumpInfo();
1758 return string;
1759 }
1760#endif
1761
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001762 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001763 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001764 SkScalar fOuterRadius;
1765 SkScalar fInnerRadius;
1766 SkScalar fOnAngle;
1767 SkScalar fTotalAngle;
1768 SkScalar fStartAngle;
1769 SkScalar fPhaseAngle;
1770 SkRect fDevBounds;
1771 };
1772
1773 SkMatrix fViewMatrixIfUsingLocalCoords;
1774 Helper fHelper;
1775 SkSTArray<1, Circle, true> fCircles;
1776 int fVertCount;
1777 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001778 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001779
Chris Daltoneb694b72020-03-16 09:25:50 -06001780 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001781 GrProgramInfo* fProgramInfo = nullptr;
1782
John Stiles7571f9e2020-09-02 22:42:33 -04001783 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784};
1785
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001786///////////////////////////////////////////////////////////////////////////////
1787
Brian Salomon05441c42017-05-15 16:45:49 -04001788class EllipseOp : public GrMeshDrawOp {
1789private:
1790 using Helper = GrSimpleMeshDrawOpHelper;
1791
1792 struct DeviceSpaceParams {
1793 SkPoint fCenter;
1794 SkScalar fXRadius;
1795 SkScalar fYRadius;
1796 SkScalar fInnerXRadius;
1797 SkScalar fInnerYRadius;
1798 };
1799
joshualitt76e7fb62015-02-11 08:52:27 -08001800public:
Brian Salomon25a88092016-12-01 09:36:50 -05001801 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001802
Herb Derbyc76d4092020-10-07 16:46:15 -04001803 static GrOp::Owner Make(GrRecordingContext* context,
1804 GrPaint&& paint,
1805 const SkMatrix& viewMatrix,
1806 const SkRect& ellipse,
1807 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001808 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001809 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001810 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1811 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001812 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1813 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001814 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1815 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1816 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1817 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001818
bsalomon4b4a7cc2016-07-08 04:42:54 -07001819 // do (potentially) anisotropic mapping of stroke
1820 SkVector scaledStroke;
1821 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001822 scaledStroke.fX = SkScalarAbs(
1823 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1824 scaledStroke.fY = SkScalarAbs(
1825 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001826
1827 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001828 bool isStrokeOnly =
1829 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001830 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1831
Brian Salomon05441c42017-05-15 16:45:49 -04001832 params.fInnerXRadius = 0;
1833 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001834 if (hasStroke) {
1835 if (SkScalarNearlyZero(scaledStroke.length())) {
1836 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1837 } else {
1838 scaledStroke.scale(SK_ScalarHalf);
1839 }
1840
1841 // we only handle thick strokes for near-circular ellipses
1842 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001843 (0.5f * params.fXRadius > params.fYRadius ||
1844 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001845 return nullptr;
1846 }
1847
1848 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001849 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1850 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1851 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1852 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001853 return nullptr;
1854 }
1855
1856 // this is legit only if scale & translation (which should be the case at the moment)
1857 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001858 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1859 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001860 }
1861
Brian Salomon05441c42017-05-15 16:45:49 -04001862 params.fXRadius += scaledStroke.fX;
1863 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001864 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001865
1866 // For large ovals with low precision floats, we fall back to the path renderer.
1867 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1868 // minimum value to avoid divides by zero. With large ovals and low precision this
1869 // leads to blurring at the edge of the oval.
1870 const SkScalar kMaxOvalRadius = 16384;
1871 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1872 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1873 return nullptr;
1874 }
1875
Greg Daniel2655ede2019-04-10 00:49:28 +00001876 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001877 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001878 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001879
Herb Derbyc76d4092020-10-07 16:46:15 -04001880 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001881 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001882 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001883 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001884 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001885 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001886 SkStrokeRec::Style style = stroke.getStyle();
1887 bool isStrokeOnly =
1888 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001889
Brian Salomon05441c42017-05-15 16:45:49 -04001890 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1891 params.fInnerXRadius, params.fInnerYRadius,
1892 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1893 params.fCenter.fY - params.fYRadius,
1894 params.fCenter.fX + params.fXRadius,
1895 params.fCenter.fY + params.fYRadius)});
1896
Greg Daniel5faf4742019-10-01 15:14:44 -04001897 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001898
Brian Salomon05441c42017-05-15 16:45:49 -04001899 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1900 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001901 }
joshualitt76e7fb62015-02-11 08:52:27 -08001902
Brian Salomon289e3d82016-12-14 15:52:56 -05001903 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001904
Robert Phillips294723d2021-06-17 09:23:58 -04001905 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001906 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001907 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001908 } else {
1909 fHelper.visitProxies(func);
1910 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001911 }
1912
Chris Dalton57ab06c2021-04-22 12:57:28 -06001913 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1914 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001915 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1916 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001917 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001918 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001919 GrProcessorAnalysisCoverage::kSingleChannel, color,
1920 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001921 }
1922
1923 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1924
bsalomone46f9fe2015-08-18 06:05:14 -07001925private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001926 GrProgramInfo* programInfo() override { return fProgramInfo; }
1927
Robert Phillips4133dc42020-03-11 15:55:55 -04001928 void onCreateProgramInfo(const GrCaps* caps,
1929 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001930 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001931 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001932 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001933 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001934 GrXferBarrierFlags renderPassXferBarriers,
1935 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001936 SkMatrix localMatrix;
1937 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001938 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001939 }
1940
Robert Phillips4490d922020-03-03 14:50:59 -05001941 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1942 fUseScale, localMatrix);
1943
Chris Dalton25da4062021-07-13 14:06:28 -06001944 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
1945 if (usesMSAASurface) {
1946 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
1947 }
1948
1949 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
1950 std::move(appliedClip),
1951 dstProxyView, gp,
1952 fHelper.detachProcessorSet(),
1953 GrPrimitiveType::kTriangles,
1954 renderPassXferBarriers,
1955 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05001956 }
1957
Robert Phillips71143952021-06-17 14:55:07 -04001958 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001959 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001960 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001961 if (!fProgramInfo) {
1962 return;
1963 }
1964 }
1965
Robert Phillips787fd9d2021-03-22 14:48:09 -04001966 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001967 GrVertexWriter verts{helper.vertices()};
1968 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001969 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001970 return;
1971 }
1972
Chris Dalton25da4062021-07-13 14:06:28 -06001973 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
1974 // full sample coverage.
1975 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
1976
Brian Salomon05441c42017-05-15 16:45:49 -04001977 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001978 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001979 SkScalar xRadius = ellipse.fXRadius;
1980 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001981
1982 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001983 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1984 SkScalarInvert(xRadius),
1985 SkScalarInvert(yRadius),
1986 SkScalarInvert(ellipse.fInnerXRadius),
1987 SkScalarInvert(ellipse.fInnerYRadius)
1988 };
Chris Dalton25da4062021-07-13 14:06:28 -06001989 SkScalar xMaxOffset = xRadius + aaBloat;
1990 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07001991
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001992 if (!fStroked) {
1993 // For filled ellipses we map a unit circle in the vertex attributes rather than
1994 // computing an ellipse and modifying that distance, so we normalize to 1
1995 xMaxOffset /= xRadius;
1996 yMaxOffset /= yRadius;
1997 }
1998
joshualitt76e7fb62015-02-11 08:52:27 -08001999 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002000 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2001 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002002 color,
2003 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002004 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002005 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002006 }
Robert Phillips4490d922020-03-03 14:50:59 -05002007 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002008 }
2009
2010 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002011 if (!fProgramInfo || !fMesh) {
2012 return;
2013 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002014
Chris Dalton765ed362020-03-16 17:34:44 -06002015 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002016 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002017 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002018 }
2019
Herb Derbye25c3002020-10-27 15:57:27 -04002020 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002021 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002022
Brian Salomon05441c42017-05-15 16:45:49 -04002023 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002024 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002025 }
2026
bsalomoncdaa97b2016-03-08 08:30:14 -08002027 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002028 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002029 }
2030
Brian Salomon05441c42017-05-15 16:45:49 -04002031 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002032 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2033 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002034 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002035 }
2036
Brian Salomon05441c42017-05-15 16:45:49 -04002037 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002038 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002039 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002040 }
2041
John Stilesaf366522020-08-13 09:57:34 -04002042#if GR_TEST_UTILS
2043 SkString onDumpInfo() const override {
2044 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2045 for (const auto& geo : fEllipses) {
2046 string.appendf(
2047 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2048 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2049 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2050 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2051 geo.fInnerXRadius, geo.fInnerYRadius);
2052 }
2053 string += fHelper.dumpInfo();
2054 return string;
2055 }
2056#endif
2057
Brian Salomon05441c42017-05-15 16:45:49 -04002058 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002059 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002060 SkScalar fXRadius;
2061 SkScalar fYRadius;
2062 SkScalar fInnerXRadius;
2063 SkScalar fInnerYRadius;
2064 SkRect fDevBounds;
2065 };
joshualitt76e7fb62015-02-11 08:52:27 -08002066
Brian Salomon289e3d82016-12-14 15:52:56 -05002067 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002068 Helper fHelper;
2069 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002070 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002071 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002072 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002073
Chris Daltoneb694b72020-03-16 09:25:50 -06002074 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002075 GrProgramInfo* fProgramInfo = nullptr;
2076
John Stiles7571f9e2020-09-02 22:42:33 -04002077 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002078};
2079
joshualitt76e7fb62015-02-11 08:52:27 -08002080/////////////////////////////////////////////////////////////////////////////////////////////////
2081
Brian Salomon05441c42017-05-15 16:45:49 -04002082class DIEllipseOp : public GrMeshDrawOp {
2083private:
2084 using Helper = GrSimpleMeshDrawOpHelper;
2085
2086 struct DeviceSpaceParams {
2087 SkPoint fCenter;
2088 SkScalar fXRadius;
2089 SkScalar fYRadius;
2090 SkScalar fInnerXRadius;
2091 SkScalar fInnerYRadius;
2092 DIEllipseStyle fStyle;
2093 };
2094
joshualitt76e7fb62015-02-11 08:52:27 -08002095public:
Brian Salomon25a88092016-12-01 09:36:50 -05002096 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002097
Herb Derbyc76d4092020-10-07 16:46:15 -04002098 static GrOp::Owner Make(GrRecordingContext* context,
2099 GrPaint&& paint,
2100 const SkMatrix& viewMatrix,
2101 const SkRect& ellipse,
2102 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002103 DeviceSpaceParams params;
2104 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2105 params.fXRadius = SkScalarHalf(ellipse.width());
2106 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002107
bsalomon4b4a7cc2016-07-08 04:42:54 -07002108 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002109 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2110 ? DIEllipseStyle::kStroke
2111 : (SkStrokeRec::kHairline_Style == style)
2112 ? DIEllipseStyle::kHairline
2113 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002114
Brian Salomon05441c42017-05-15 16:45:49 -04002115 params.fInnerXRadius = 0;
2116 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002117 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2118 SkScalar strokeWidth = stroke.getWidth();
2119
2120 if (SkScalarNearlyZero(strokeWidth)) {
2121 strokeWidth = SK_ScalarHalf;
2122 } else {
2123 strokeWidth *= SK_ScalarHalf;
2124 }
2125
2126 // we only handle thick strokes for near-circular ellipses
2127 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002128 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2129 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002130 return nullptr;
2131 }
2132
2133 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002134 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2135 (strokeWidth * strokeWidth) * params.fXRadius) {
2136 return nullptr;
2137 }
2138 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2139 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002140 return nullptr;
2141 }
2142
2143 // set inner radius (if needed)
2144 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002145 params.fInnerXRadius = params.fXRadius - strokeWidth;
2146 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002147 }
2148
Brian Salomon05441c42017-05-15 16:45:49 -04002149 params.fXRadius += strokeWidth;
2150 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002151 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002152
2153 // For large ovals with low precision floats, we fall back to the path renderer.
2154 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2155 // minimum value to avoid divides by zero. With large ovals and low precision this
2156 // leads to blurring at the edge of the oval.
2157 const SkScalar kMaxOvalRadius = 16384;
2158 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2159 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2160 return nullptr;
2161 }
2162
Brian Salomon05441c42017-05-15 16:45:49 -04002163 if (DIEllipseStyle::kStroke == params.fStyle &&
2164 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2165 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002166 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002167 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002168 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002169
Herb Derbyc76d4092020-10-07 16:46:15 -04002170 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002171 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002172 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002173 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002174 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002175 // This expands the outer rect so that after CTM we end up with a half-pixel border
2176 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2177 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2178 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2179 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002180 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2181 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002182
Brian Salomon05441c42017-05-15 16:45:49 -04002183 fEllipses.emplace_back(
2184 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2185 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002186 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2187 params.fCenter.fY - params.fYRadius,
2188 params.fCenter.fX + params.fXRadius,
2189 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002190 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002191 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002192 }
2193
Brian Salomon289e3d82016-12-14 15:52:56 -05002194 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002195
Robert Phillips294723d2021-06-17 09:23:58 -04002196 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002197 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002198 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002199 } else {
2200 fHelper.visitProxies(func);
2201 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002202 }
2203
Chris Dalton57ab06c2021-04-22 12:57:28 -06002204 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2205 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002206 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2207 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002208 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002209 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002210 GrProcessorAnalysisCoverage::kSingleChannel, color,
2211 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002212 }
2213
2214 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2215
bsalomone46f9fe2015-08-18 06:05:14 -07002216private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002217 GrProgramInfo* programInfo() override { return fProgramInfo; }
2218
Robert Phillips4133dc42020-03-11 15:55:55 -04002219 void onCreateProgramInfo(const GrCaps* caps,
2220 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002221 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002222 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002223 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002224 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002225 GrXferBarrierFlags renderPassXferBarriers,
2226 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002227 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2228 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002229 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002230
Chris Dalton25da4062021-07-13 14:06:28 -06002231 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2232 if (usesMSAASurface) {
2233 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2234 }
2235
2236 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2237 std::move(appliedClip),
2238 dstProxyView, gp,
2239 fHelper.detachProcessorSet(),
2240 GrPrimitiveType::kTriangles,
2241 renderPassXferBarriers,
2242 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002243 }
2244
Robert Phillips71143952021-06-17 14:55:07 -04002245 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002246 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002247 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002248 }
2249
Robert Phillips787fd9d2021-03-22 14:48:09 -04002250 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002251 GrVertexWriter verts{helper.vertices()};
2252 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002253 return;
2254 }
2255
Brian Salomon05441c42017-05-15 16:45:49 -04002256 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002257 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002258 SkScalar xRadius = ellipse.fXRadius;
2259 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002260
Chris Dalton25da4062021-07-13 14:06:28 -06002261 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2262 // full sample coverage.
2263 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2264 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2265 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002266
Chris Dalton25da4062021-07-13 14:06:28 -06002267 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2268 // occurs at x^2 + y^2 == 1.
2269 float outerCoordX = drawBounds.width() / (xRadius * 2);
2270 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002271
Chris Dalton25da4062021-07-13 14:06:28 -06002272 // By default, constructed so that inner coord is (0, 0) for all points
2273 float innerCoordX = 0;
2274 float innerCoordY = 0;
2275
2276 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2277 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002278 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002279 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2280 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002281 }
joshualitt76e7fb62015-02-11 08:52:27 -08002282
Chris Dalton25da4062021-07-13 14:06:28 -06002283 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002284 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002285 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002286 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002287 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002288 }
Robert Phillips4490d922020-03-03 14:50:59 -05002289 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002290 }
2291
2292 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002293 if (!fProgramInfo || !fMesh) {
2294 return;
2295 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002296
Chris Dalton765ed362020-03-16 17:34:44 -06002297 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002298 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002299 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002300 }
halcanary9d524f22016-03-29 09:03:52 -07002301
Herb Derbye25c3002020-10-27 15:57:27 -04002302 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002303 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002304 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002305 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002306 }
2307
bsalomoncdaa97b2016-03-08 08:30:14 -08002308 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002309 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002310 }
2311
joshualittd96a67b2015-05-05 14:09:05 -07002312 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002313 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002314 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002315 }
2316
Brian Salomon05441c42017-05-15 16:45:49 -04002317 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002318 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002319 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002320 }
2321
John Stilesaf366522020-08-13 09:57:34 -04002322#if GR_TEST_UTILS
2323 SkString onDumpInfo() const override {
2324 SkString string;
2325 for (const auto& geo : fEllipses) {
2326 string.appendf(
2327 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2328 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2329 "GeoDY: %.2f\n",
2330 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2331 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2332 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2333 }
2334 string += fHelper.dumpInfo();
2335 return string;
2336 }
2337#endif
2338
Brian Salomon05441c42017-05-15 16:45:49 -04002339 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2340 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002341
Brian Salomon05441c42017-05-15 16:45:49 -04002342 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002343 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002344 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002345 SkScalar fXRadius;
2346 SkScalar fYRadius;
2347 SkScalar fInnerXRadius;
2348 SkScalar fInnerYRadius;
2349 SkScalar fGeoDx;
2350 SkScalar fGeoDy;
2351 DIEllipseStyle fStyle;
2352 SkRect fBounds;
2353 };
2354
Brian Salomon05441c42017-05-15 16:45:49 -04002355 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002356 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002357 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002358 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002359
Chris Daltoneb694b72020-03-16 09:25:50 -06002360 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002361 GrProgramInfo* fProgramInfo = nullptr;
2362
John Stiles7571f9e2020-09-02 22:42:33 -04002363 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002364};
2365
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002366///////////////////////////////////////////////////////////////////////////////
2367
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002368// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002369//
2370// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2371// ____________
2372// |_|________|_|
2373// | | | |
2374// | | | |
2375// | | | |
2376// |_|________|_|
2377// |_|________|_|
2378//
2379// For strokes, we don't draw the center quad.
2380//
2381// For circular roundrects, in the case where the stroke width is greater than twice
2382// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002383// in the center. The shared vertices are duplicated so we can set a different outer radius
2384// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002385// ____________
2386// |_|________|_|
2387// | |\ ____ /| |
2388// | | | | | |
2389// | | |____| | |
2390// |_|/______\|_|
2391// |_|________|_|
2392//
2393// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002394//
2395// For filled rrects that need to provide a distance vector we resuse the overstroke
2396// geometry but make the inner rect degenerate (either a point or a horizontal or
2397// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002398
jvanverth84839f62016-08-29 10:16:40 -07002399static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002400 // clang-format off
2401 // overstroke quads
2402 // we place this at the beginning so that we can skip these indices when rendering normally
2403 16, 17, 19, 16, 19, 18,
2404 19, 17, 23, 19, 23, 21,
2405 21, 23, 22, 21, 22, 20,
2406 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002407
Brian Salomon289e3d82016-12-14 15:52:56 -05002408 // corners
2409 0, 1, 5, 0, 5, 4,
2410 2, 3, 7, 2, 7, 6,
2411 8, 9, 13, 8, 13, 12,
2412 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002413
Brian Salomon289e3d82016-12-14 15:52:56 -05002414 // edges
2415 1, 2, 6, 1, 6, 5,
2416 4, 5, 9, 4, 9, 8,
2417 6, 7, 11, 6, 11, 10,
2418 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002419
Brian Salomon289e3d82016-12-14 15:52:56 -05002420 // center
2421 // we place this at the end so that we can ignore these indices when not rendering as filled
2422 5, 6, 10, 5, 10, 9,
2423 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002424};
Brian Salomon289e3d82016-12-14 15:52:56 -05002425
jvanverth84839f62016-08-29 10:16:40 -07002426// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002427static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002428
jvanverth84839f62016-08-29 10:16:40 -07002429// overstroke count is arraysize minus the center indices
2430static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2431// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002432static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002433// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002434static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2435static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002436static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002437
jvanverthc3d0e422016-08-25 08:12:35 -07002438enum RRectType {
2439 kFill_RRectType,
2440 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002441 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002442};
2443
jvanverth84839f62016-08-29 10:16:40 -07002444static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002445 switch (type) {
2446 case kFill_RRectType:
2447 case kStroke_RRectType:
2448 return kVertsPerStandardRRect;
2449 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002450 return kVertsPerOverstrokeRRect;
2451 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002452 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002453}
2454
2455static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002456 switch (type) {
2457 case kFill_RRectType:
2458 return kIndicesPerFillRRect;
2459 case kStroke_RRectType:
2460 return kIndicesPerStrokeRRect;
2461 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002462 return kIndicesPerOverstrokeRRect;
2463 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002464 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002465}
2466
2467static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002468 switch (type) {
2469 case kFill_RRectType:
2470 case kStroke_RRectType:
2471 return gStandardRRectIndices;
2472 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002473 return gOverstrokeRRectIndices;
2474 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002475 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002476}
2477
joshualitt76e7fb62015-02-11 08:52:27 -08002478///////////////////////////////////////////////////////////////////////////////////////////////////
2479
Robert Phillips79839d42016-10-06 15:03:34 -04002480// For distance computations in the interior of filled rrects we:
2481//
2482// add a interior degenerate (point or line) rect
2483// each vertex of that rect gets -outerRad as its radius
2484// this makes the computation of the distance to the outer edge be negative
2485// negative values are caught and then handled differently in the GP's onEmitCode
2486// each vertex is also given the normalized x & y distance from the interior rect's edge
2487// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2488
Brian Salomon05441c42017-05-15 16:45:49 -04002489class CircularRRectOp : public GrMeshDrawOp {
2490private:
2491 using Helper = GrSimpleMeshDrawOpHelper;
2492
joshualitt76e7fb62015-02-11 08:52:27 -08002493public:
Brian Salomon25a88092016-12-01 09:36:50 -05002494 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002495
bsalomon4b4a7cc2016-07-08 04:42:54 -07002496 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2497 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002498 static GrOp::Owner Make(GrRecordingContext* context,
2499 GrPaint&& paint,
2500 const SkMatrix& viewMatrix,
2501 const SkRect& devRect,
2502 float devRadius,
2503 float devStrokeWidth,
2504 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002505 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002506 devRect, devRadius,
2507 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002508 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002509 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002510 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2511 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002512 : INHERITED(ClassID())
2513 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002514 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002515 SkRect bounds = devRect;
2516 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2517 SkScalar innerRadius = 0.0f;
2518 SkScalar outerRadius = devRadius;
2519 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002520 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002521 if (devStrokeWidth > 0) {
2522 if (SkScalarNearlyZero(devStrokeWidth)) {
2523 halfWidth = SK_ScalarHalf;
2524 } else {
2525 halfWidth = SkScalarHalf(devStrokeWidth);
2526 }
joshualitt76e7fb62015-02-11 08:52:27 -08002527
bsalomon4b4a7cc2016-07-08 04:42:54 -07002528 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002529 // Outset stroke by 1/4 pixel
2530 devStrokeWidth += 0.25f;
2531 // If stroke is greater than width or height, this is still a fill
2532 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002533 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002534 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002535 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002536 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002537 }
2538 outerRadius += halfWidth;
2539 bounds.outset(halfWidth, halfWidth);
2540 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002541
Greg Daniel2655ede2019-04-10 00:49:28 +00002542 // The radii are outset for two reasons. First, it allows the shader to simply perform
2543 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2544 // Second, the outer radius is used to compute the verts of the bounding box that is
2545 // rendered and the outset ensures the box will cover all partially covered by the rrect
2546 // corners.
2547 outerRadius += SK_ScalarHalf;
2548 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002549
Greg Daniel5faf4742019-10-01 15:14:44 -04002550 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002551
Greg Daniel2655ede2019-04-10 00:49:28 +00002552 // Expand the rect for aa to generate correct vertices.
2553 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002554
Brian Salomon05441c42017-05-15 16:45:49 -04002555 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002556 fVertCount = rrect_type_to_vert_count(type);
2557 fIndexCount = rrect_type_to_index_count(type);
2558 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002559 }
2560
Brian Salomon289e3d82016-12-14 15:52:56 -05002561 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002562
Robert Phillips294723d2021-06-17 09:23:58 -04002563 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002564 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002565 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002566 } else {
2567 fHelper.visitProxies(func);
2568 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002569 }
2570
Chris Dalton57ab06c2021-04-22 12:57:28 -06002571 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2572 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002573 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002574 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002575 GrProcessorAnalysisCoverage::kSingleChannel, color,
2576 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002577 }
2578
2579 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2580
Brian Salomon92aee3d2016-12-21 09:20:25 -05002581private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002582 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002583 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002584 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002585 SkASSERT(smInset < bigInset);
2586
2587 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002588 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2589 color,
2590 xOffset, 0.0f,
2591 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002592
2593 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002594 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2595 color,
2596 xOffset, 0.0f,
2597 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002598
Brian Osmana1d4eb92018-12-06 16:33:10 -05002599 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2600 color,
2601 0.0f, 0.0f,
2602 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002603
Brian Osmana1d4eb92018-12-06 16:33:10 -05002604 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2605 color,
2606 0.0f, 0.0f,
2607 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002608
Brian Osmana1d4eb92018-12-06 16:33:10 -05002609 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2610 color,
2611 0.0f, 0.0f,
2612 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002613
Brian Osmana1d4eb92018-12-06 16:33:10 -05002614 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2615 color,
2616 0.0f, 0.0f,
2617 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002618
2619 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002620 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2621 color,
2622 xOffset, 0.0f,
2623 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002624
2625 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002626 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2627 color,
2628 xOffset, 0.0f,
2629 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002630 }
2631
Robert Phillips2669a7b2020-03-12 12:07:19 -04002632 GrProgramInfo* programInfo() override { return fProgramInfo; }
2633
Robert Phillips4133dc42020-03-11 15:55:55 -04002634 void onCreateProgramInfo(const GrCaps* caps,
2635 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002636 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002637 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002638 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002639 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002640 GrXferBarrierFlags renderPassXferBarriers,
2641 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002642 SkASSERT(!usesMSAASurface);
2643
bsalomoncdaa97b2016-03-08 08:30:14 -08002644 // Invert the view matrix as a local matrix (if any other processors require coords).
2645 SkMatrix localMatrix;
2646 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002647 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002648 }
2649
Robert Phillips4490d922020-03-03 14:50:59 -05002650 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002651 false, false, false, false,
2652 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002653
Brian Salomon8afde5f2020-04-01 16:22:00 -04002654 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002655 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002656 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002657 }
2658
Robert Phillips71143952021-06-17 14:55:07 -04002659 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002660 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002661 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002662 if (!fProgramInfo) {
2663 return;
2664 }
2665 }
2666
Brian Salomon12d22642019-01-29 14:38:50 -05002667 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002668 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002669
Robert Phillips787fd9d2021-03-22 14:48:09 -04002670 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002671 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002672 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002673 SkDebugf("Could not allocate vertices\n");
2674 return;
2675 }
2676
Brian Salomon12d22642019-01-29 14:38:50 -05002677 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002678 int firstIndex = 0;
2679 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2680 if (!indices) {
2681 SkDebugf("Could not allocate indices\n");
2682 return;
2683 }
2684
2685 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002686 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002687 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002688 SkScalar outerRadius = rrect.fOuterRadius;
2689 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002690
Brian Salomon289e3d82016-12-14 15:52:56 -05002691 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2692 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002693
Brian Salomon289e3d82016-12-14 15:52:56 -05002694 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002695 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002696 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002697 SkScalar innerRadius = rrect.fType != kFill_RRectType
2698 ? rrect.fInnerRadius / rrect.fOuterRadius
2699 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002700 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002701 verts.write(bounds.fLeft, yCoords[i],
2702 color,
2703 -1.0f, yOuterRadii[i],
2704 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002705
Brian Osmana1d4eb92018-12-06 16:33:10 -05002706 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2707 color,
2708 0.0f, yOuterRadii[i],
2709 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002710
Brian Osmana1d4eb92018-12-06 16:33:10 -05002711 verts.write(bounds.fRight - outerRadius, yCoords[i],
2712 color,
2713 0.0f, yOuterRadii[i],
2714 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002715
Brian Osmana1d4eb92018-12-06 16:33:10 -05002716 verts.write(bounds.fRight, yCoords[i],
2717 color,
2718 1.0f, yOuterRadii[i],
2719 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002720 }
jvanverthc3d0e422016-08-25 08:12:35 -07002721 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002722 // Effectively this is an additional stroked rrect, with its
2723 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2724 // This will give us correct AA in the center and the correct
2725 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002726 //
jvanvertha4f1af82016-08-29 07:17:47 -07002727 // Also, the outer offset is a constant vector pointing to the right, which
2728 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002729 if (kOverstroke_RRectType == rrect.fType) {
2730 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002731
Brian Salomon05441c42017-05-15 16:45:49 -04002732 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002733 // this is the normalized distance from the outer rectangle of this
2734 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002735 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002736
Brian Osmana1d4eb92018-12-06 16:33:10 -05002737 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002738 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002739 }
jvanverth6a397612016-08-26 08:15:33 -07002740
Brian Salomon05441c42017-05-15 16:45:49 -04002741 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2742 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002743 for (int i = 0; i < primIndexCount; ++i) {
2744 *indices++ = primIndices[i] + currStartVertex;
2745 }
2746
Brian Salomon05441c42017-05-15 16:45:49 -04002747 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002748 }
2749
Robert Phillips4490d922020-03-03 14:50:59 -05002750 fMesh = target->allocMesh();
2751 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002752 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002753 }
2754
2755 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002756 if (!fProgramInfo || !fMesh) {
2757 return;
2758 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002759
Chris Dalton765ed362020-03-16 17:34:44 -06002760 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002761 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002762 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002763 }
2764
Herb Derbye25c3002020-10-27 15:57:27 -04002765 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002766 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002767
2768 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002769 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002770 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002771 }
2772
Brian Salomon05441c42017-05-15 16:45:49 -04002773 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002774 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002775 }
2776
Brian Salomon05441c42017-05-15 16:45:49 -04002777 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002778 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2779 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002780 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002781 }
2782
Brian Salomon05441c42017-05-15 16:45:49 -04002783 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002784 fVertCount += that->fVertCount;
2785 fIndexCount += that->fIndexCount;
2786 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002787 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002788 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002789 }
2790
John Stilesaf366522020-08-13 09:57:34 -04002791#if GR_TEST_UTILS
2792 SkString onDumpInfo() const override {
2793 SkString string;
2794 for (int i = 0; i < fRRects.count(); ++i) {
2795 string.appendf(
2796 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2797 "InnerRad: %.2f, OuterRad: %.2f\n",
2798 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2799 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2800 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2801 fRRects[i].fOuterRadius);
2802 }
2803 string += fHelper.dumpInfo();
2804 return string;
2805 }
2806#endif
2807
Brian Salomon05441c42017-05-15 16:45:49 -04002808 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002809 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002810 SkScalar fInnerRadius;
2811 SkScalar fOuterRadius;
2812 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002813 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002814 };
2815
Brian Salomon289e3d82016-12-14 15:52:56 -05002816 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002817 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002818 int fVertCount;
2819 int fIndexCount;
2820 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002821 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002822 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002823
Chris Daltoneb694b72020-03-16 09:25:50 -06002824 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002825 GrProgramInfo* fProgramInfo = nullptr;
2826
John Stiles7571f9e2020-09-02 22:42:33 -04002827 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002828};
2829
jvanverth84839f62016-08-29 10:16:40 -07002830static const int kNumRRectsInIndexBuffer = 256;
2831
2832GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2833GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002834static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2835 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002836 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2837 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2838 switch (type) {
2839 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002840 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002841 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2842 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002843 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002844 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002845 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2846 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002847 default:
2848 SkASSERT(false);
2849 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002850 }
jvanverth84839f62016-08-29 10:16:40 -07002851}
2852
Brian Salomon05441c42017-05-15 16:45:49 -04002853class EllipticalRRectOp : public GrMeshDrawOp {
2854private:
2855 using Helper = GrSimpleMeshDrawOpHelper;
2856
joshualitt76e7fb62015-02-11 08:52:27 -08002857public:
Brian Salomon25a88092016-12-01 09:36:50 -05002858 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002859
bsalomon4b4a7cc2016-07-08 04:42:54 -07002860 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2861 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002862 static GrOp::Owner Make(GrRecordingContext* context,
2863 GrPaint&& paint,
2864 const SkMatrix& viewMatrix,
2865 const SkRect& devRect,
2866 float devXRadius,
2867 float devYRadius,
2868 SkVector devStrokeWidths,
2869 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002870 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2871 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002872 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2873 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002874 if (devStrokeWidths.fX > 0) {
2875 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2876 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2877 } else {
2878 devStrokeWidths.scale(SK_ScalarHalf);
2879 }
joshualitt76e7fb62015-02-11 08:52:27 -08002880
bsalomon4b4a7cc2016-07-08 04:42:54 -07002881 // we only handle thick strokes for near-circular ellipses
2882 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002883 (SK_ScalarHalf * devXRadius > devYRadius ||
2884 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002885 return nullptr;
2886 }
2887
2888 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002889 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2890 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002891 return nullptr;
2892 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002893 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2894 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002895 return nullptr;
2896 }
Brian Salomon05441c42017-05-15 16:45:49 -04002897 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002898 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002899 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002900 devXRadius, devYRadius, devStrokeWidths,
2901 strokeOnly);
2902 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903
Herb Derbyc76d4092020-10-07 16:46:15 -04002904 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002905 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2906 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002907 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002908 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002909 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002910 SkScalar innerXRadius = 0.0f;
2911 SkScalar innerYRadius = 0.0f;
2912 SkRect bounds = devRect;
2913 bool stroked = false;
2914 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002915 // this is legit only if scale & translation (which should be the case at the moment)
2916 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002917 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2918 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002919 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2920 }
2921
Brian Salomon05441c42017-05-15 16:45:49 -04002922 devXRadius += devStrokeHalfWidths.fX;
2923 devYRadius += devStrokeHalfWidths.fY;
2924 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002925 }
2926
Brian Salomon05441c42017-05-15 16:45:49 -04002927 fStroked = stroked;
2928 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002929 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002930 fRRects.emplace_back(
2931 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002932 }
2933
Brian Salomon289e3d82016-12-14 15:52:56 -05002934 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002935
Robert Phillips294723d2021-06-17 09:23:58 -04002936 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002937 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002938 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002939 } else {
2940 fHelper.visitProxies(func);
2941 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002942 }
2943
Chris Dalton57ab06c2021-04-22 12:57:28 -06002944 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2945 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002946 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002947 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002948 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002949 GrProcessorAnalysisCoverage::kSingleChannel, color,
2950 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002951 }
2952
2953 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2954
bsalomone46f9fe2015-08-18 06:05:14 -07002955private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002956 GrProgramInfo* programInfo() override { return fProgramInfo; }
2957
Robert Phillips4133dc42020-03-11 15:55:55 -04002958 void onCreateProgramInfo(const GrCaps* caps,
2959 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002960 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002961 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002962 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002963 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002964 GrXferBarrierFlags renderPassXferBarriers,
2965 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002966 SkMatrix localMatrix;
2967 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002968 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002969 }
2970
Robert Phillips4490d922020-03-03 14:50:59 -05002971 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2972 fUseScale, localMatrix);
2973
Chris Dalton25da4062021-07-13 14:06:28 -06002974 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2975 if (usesMSAASurface) {
2976 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2977 }
2978
2979 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2980 std::move(appliedClip),
2981 dstProxyView, gp,
2982 fHelper.detachProcessorSet(),
2983 GrPrimitiveType::kTriangles,
2984 renderPassXferBarriers,
2985 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002986 }
2987
Robert Phillips71143952021-06-17 14:55:07 -04002988 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002989 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002990 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002991 if (!fProgramInfo) {
2992 return;
2993 }
2994 }
joshualitt76e7fb62015-02-11 08:52:27 -08002995
bsalomonb5238a72015-05-05 07:49:49 -07002996 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002997 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002998 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2999 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003000
Brian Salomon12d22642019-01-29 14:38:50 -05003001 if (!indexBuffer) {
3002 SkDebugf("Could not allocate indices\n");
3003 return;
3004 }
Robert Phillips4490d922020-03-03 14:50:59 -05003005 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003006 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003007 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003008 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003009 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003010 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003011 SkDebugf("Could not allocate vertices\n");
3012 return;
3013 }
3014
Brian Salomon05441c42017-05-15 16:45:49 -04003015 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003016 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003017 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003018 float reciprocalRadii[4] = {
3019 SkScalarInvert(rrect.fXRadius),
3020 SkScalarInvert(rrect.fYRadius),
3021 SkScalarInvert(rrect.fInnerXRadius),
3022 SkScalarInvert(rrect.fInnerYRadius)
3023 };
joshualitt76e7fb62015-02-11 08:52:27 -08003024
Brian Osmane3afdd52020-10-28 10:49:56 -04003025 // If the stroke width is exactly double the radius, the inner radii will be zero.
3026 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3027 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3028 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3029
Chris Dalton25da4062021-07-13 14:06:28 -06003030 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3031 // full sample coverage.
3032 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3033
3034 // Extend out the radii to antialias.
3035 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3036 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003037
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003038 SkScalar xMaxOffset = xOuterRadius;
3039 SkScalar yMaxOffset = yOuterRadius;
3040 if (!fStroked) {
3041 // For filled rrects we map a unit circle in the vertex attributes rather than
3042 // computing an ellipse and modifying that distance, so we normalize to 1.
3043 xMaxOffset /= rrect.fXRadius;
3044 yMaxOffset /= rrect.fYRadius;
3045 }
3046
Chris Dalton25da4062021-07-13 14:06:28 -06003047 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003048
Brian Salomon289e3d82016-12-14 15:52:56 -05003049 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3050 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003051 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003052 SK_ScalarNearlyZero, // we're using inversesqrt() in
3053 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003054 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003055
Brian Osman788b9162020-02-07 10:36:46 -05003056 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003057 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 verts.write(bounds.fLeft, yCoords[i],
3059 color,
3060 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003061 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003062 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003063
Brian Osmana1d4eb92018-12-06 16:33:10 -05003064 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3065 color,
3066 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003067 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003068 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003069
Brian Osmana1d4eb92018-12-06 16:33:10 -05003070 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3071 color,
3072 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003073 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003074 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003075
Brian Osmana1d4eb92018-12-06 16:33:10 -05003076 verts.write(bounds.fRight, yCoords[i],
3077 color,
3078 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003079 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003080 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003081 }
3082 }
Robert Phillips4490d922020-03-03 14:50:59 -05003083 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003084 }
3085
3086 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003087 if (!fProgramInfo || !fMesh) {
3088 return;
3089 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003090
Chris Dalton765ed362020-03-16 17:34:44 -06003091 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003092 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003093 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003094 }
3095
Herb Derbye25c3002020-10-27 15:57:27 -04003096 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003097 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003098
Brian Salomon05441c42017-05-15 16:45:49 -04003099 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003100 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003101 }
3102
bsalomoncdaa97b2016-03-08 08:30:14 -08003103 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003104 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003105 }
3106
Brian Salomon05441c42017-05-15 16:45:49 -04003107 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003108 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3109 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003110 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003111 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003112
Brian Salomon05441c42017-05-15 16:45:49 -04003113 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003114 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003115 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003116 }
3117
John Stilesaf366522020-08-13 09:57:34 -04003118#if GR_TEST_UTILS
3119 SkString onDumpInfo() const override {
3120 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3121 for (const auto& geo : fRRects) {
3122 string.appendf(
3123 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3124 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3125 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3126 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3127 geo.fInnerXRadius, geo.fInnerYRadius);
3128 }
3129 string += fHelper.dumpInfo();
3130 return string;
3131 }
3132#endif
3133
Brian Salomon05441c42017-05-15 16:45:49 -04003134 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003135 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003136 SkScalar fXRadius;
3137 SkScalar fYRadius;
3138 SkScalar fInnerXRadius;
3139 SkScalar fInnerYRadius;
3140 SkRect fDevBounds;
3141 };
3142
Brian Salomon289e3d82016-12-14 15:52:56 -05003143 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003144 Helper fHelper;
3145 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003146 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003147 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003148 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003149
Chris Daltoneb694b72020-03-16 09:25:50 -06003150 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003151 GrProgramInfo* fProgramInfo = nullptr;
3152
John Stiles7571f9e2020-09-02 22:42:33 -04003153 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003154};
3155
Herb Derbyc76d4092020-10-07 16:46:15 -04003156GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3157 GrPaint&& paint,
3158 const SkMatrix& viewMatrix,
3159 const SkRRect& rrect,
3160 const SkStrokeRec& stroke,
3161 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003162 SkASSERT(viewMatrix.rectStaysRect());
3163 SkASSERT(viewMatrix.isSimilarity());
3164 SkASSERT(rrect.isSimple());
3165 SkASSERT(!rrect.isOval());
3166 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3167
3168 // RRect ops only handle simple, but not too simple, rrects.
3169 // Do any matrix crunching before we reset the draw state for device coords.
3170 const SkRect& rrectBounds = rrect.getBounds();
3171 SkRect bounds;
3172 viewMatrix.mapRect(&bounds, rrectBounds);
3173
3174 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3175 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3176 viewMatrix[SkMatrix::kMSkewY]));
3177
3178 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3179 SkScalar scaledStroke = -1;
3180 SkScalar strokeWidth = stroke.getWidth();
3181 SkStrokeRec::Style style = stroke.getStyle();
3182
3183 bool isStrokeOnly =
3184 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3185 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3186
3187 if (hasStroke) {
3188 if (SkStrokeRec::kHairline_Style == style) {
3189 scaledStroke = SK_Scalar1;
3190 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003191 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3192 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003193 }
3194 }
3195
3196 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3197 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3198 // patch will have fractional coverage. This only matters when the interior is actually filled.
3199 // We could consider falling back to rect rendering here, since a tiny radius is
3200 // indistinguishable from a square corner.
3201 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3202 return nullptr;
3203 }
3204
3205 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3206 scaledStroke, isStrokeOnly);
3207}
3208
Herb Derbyc76d4092020-10-07 16:46:15 -04003209GrOp::Owner make_rrect_op(GrRecordingContext* context,
3210 GrPaint&& paint,
3211 const SkMatrix& viewMatrix,
3212 const SkRRect& rrect,
3213 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003214 SkASSERT(viewMatrix.rectStaysRect());
3215 SkASSERT(rrect.isSimple());
3216 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003217
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003218 // RRect ops only handle simple, but not too simple, rrects.
3219 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003220 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003221 SkRect bounds;
3222 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003223
Mike Reed242135a2018-02-22 13:41:39 -05003224 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003225 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3226 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3227 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3228 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003229
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003230 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003231
bsalomon4b4a7cc2016-07-08 04:42:54 -07003232 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3233 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003234 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235
Brian Salomon289e3d82016-12-14 15:52:56 -05003236 bool isStrokeOnly =
3237 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003238 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3239
3240 if (hasStroke) {
3241 if (SkStrokeRec::kHairline_Style == style) {
3242 scaledStroke.set(1, 1);
3243 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003244 scaledStroke.fX = SkScalarAbs(
3245 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3246 scaledStroke.fY = SkScalarAbs(
3247 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003248 }
3249
Jim Van Verth64b85892019-06-17 12:01:46 -04003250 // if half of strokewidth is greater than radius, we don't handle that right now
3251 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3252 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003253 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003254 }
3255 }
3256
Brian Salomon8a97f562019-04-18 14:07:27 -04003257 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003258 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003259 std::swap(xRadius, yRadius);
3260 std::swap(scaledStroke.fX, scaledStroke.fY);
3261 }
3262
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003263 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3264 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3265 // patch will have fractional coverage. This only matters when the interior is actually filled.
3266 // We could consider falling back to rect rendering here, since a tiny radius is
3267 // indistinguishable from a square corner.
3268 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003269 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003270 }
3271
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003272 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003273 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3274 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003275}
3276
Herb Derbyc76d4092020-10-07 16:46:15 -04003277GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3278 GrPaint&& paint,
3279 const SkMatrix& viewMatrix,
3280 const SkRRect& rrect,
3281 const SkStrokeRec& stroke,
3282 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003283 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003284 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003285 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003286 }
3287
3288 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003289 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003290 }
3291
Greg Daniel2655ede2019-04-10 00:49:28 +00003292 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003293}
joshualitt3e708c52015-04-30 13:49:27 -07003294
bsalomon4b4a7cc2016-07-08 04:42:54 -07003295///////////////////////////////////////////////////////////////////////////////
3296
Herb Derbyc76d4092020-10-07 16:46:15 -04003297GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3298 GrPaint&& paint,
3299 const SkMatrix& viewMatrix,
3300 const SkRect& oval,
3301 const GrStyle& style,
3302 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003303 SkScalar width = oval.width();
3304 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3305 circle_stays_circle(viewMatrix));
3306
3307 auto r = width / 2.f;
3308 SkPoint center = { oval.centerX(), oval.centerY() };
3309 if (style.hasNonDashPathEffect()) {
3310 return nullptr;
3311 } else if (style.isDashed()) {
3312 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3313 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3314 return nullptr;
3315 }
3316 auto onInterval = style.dashIntervals()[0];
3317 auto offInterval = style.dashIntervals()[1];
3318 if (offInterval == 0) {
3319 GrStyle strokeStyle(style.strokeRec(), nullptr);
3320 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3321 strokeStyle, shaderCaps);
3322 } else if (onInterval == 0) {
3323 // There is nothing to draw but we have no way to indicate that here.
3324 return nullptr;
3325 }
3326 auto angularOnInterval = onInterval / r;
3327 auto angularOffInterval = offInterval / r;
3328 auto phaseAngle = style.dashPhase() / r;
3329 // Currently this function doesn't accept ovals with different start angles, though
3330 // it could.
3331 static const SkScalar kStartAngle = 0.f;
3332 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3333 style.strokeRec().getWidth(), kStartAngle,
3334 angularOnInterval, angularOffInterval, phaseAngle);
3335 }
3336 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3337}
3338
Herb Derbyc76d4092020-10-07 16:46:15 -04003339GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3340 GrPaint&& paint,
3341 const SkMatrix& viewMatrix,
3342 const SkRect& oval,
3343 const GrStyle& style,
3344 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003345 if (style.pathEffect()) {
3346 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003347 }
3348
Stan Ilieveb868aa2017-02-21 11:06:16 -05003349 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003350 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003351 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003352 }
3353
Stan Ilieveb868aa2017-02-21 11:06:16 -05003354 // Otherwise, if we have shader derivative support, render as device-independent
3355 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003356 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3357 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3358 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3359 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3360 // Check for near-degenerate matrix
3361 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003362 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003363 style.strokeRec());
3364 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003365 }
3366
bsalomon4b4a7cc2016-07-08 04:42:54 -07003367 return nullptr;
3368}
3369
3370///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003371
Herb Derbyc76d4092020-10-07 16:46:15 -04003372GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3373 GrPaint&& paint,
3374 const SkMatrix& viewMatrix,
3375 const SkRect& oval, SkScalar startAngle,
3376 SkScalar sweepAngle, bool useCenter,
3377 const GrStyle& style,
3378 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003379 SkASSERT(!oval.isEmpty());
3380 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003381 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003382 if (SkScalarAbs(sweepAngle) >= 360.f) {
3383 return nullptr;
3384 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003385 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3386 return nullptr;
3387 }
3388 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003389 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3390 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003391 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003392 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003393}
3394
3395///////////////////////////////////////////////////////////////////////////////
3396
Hal Canary6f6961e2017-01-31 13:50:44 -05003397#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003398
Brian Salomon05441c42017-05-15 16:45:49 -04003399GR_DRAW_OP_TEST_DEFINE(CircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003400 if (numSamples > 1) {
3401 return nullptr;
3402 }
3403
bsalomon4f3a0ca2016-08-22 13:14:26 -07003404 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003405 SkScalar rotate = random->nextSScalar1() * 360.f;
3406 SkScalar translateX = random->nextSScalar1() * 1000.f;
3407 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003408 SkScalar scale;
3409 do {
3410 scale = random->nextSScalar1() * 100.f;
3411 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003412 SkMatrix viewMatrix;
3413 viewMatrix.setRotate(rotate);
3414 viewMatrix.postTranslate(translateX, translateY);
3415 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003416 SkRect circle = GrTest::TestSquare(random);
3417 SkPoint center = {circle.centerX(), circle.centerY()};
3418 SkScalar radius = circle.width() / 2.f;
3419 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003420 CircleOp::ArcParams arcParamsTmp;
3421 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003422 if (random->nextBool()) {
3423 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003424 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3425 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003426 arcParams = &arcParamsTmp;
3427 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003428 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3429 center, radius,
3430 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003431 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003432 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003433 }
Mike Klein16885072018-12-11 09:54:31 -05003434 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003435 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003436}
3437
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003438GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003439 if (numSamples > 1) {
3440 return nullptr;
3441 }
3442
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003443 SkScalar rotate = random->nextSScalar1() * 360.f;
3444 SkScalar translateX = random->nextSScalar1() * 1000.f;
3445 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003446 SkScalar scale;
3447 do {
3448 scale = random->nextSScalar1() * 100.f;
3449 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003450 SkMatrix viewMatrix;
3451 viewMatrix.setRotate(rotate);
3452 viewMatrix.postTranslate(translateX, translateY);
3453 viewMatrix.postScale(scale, scale);
3454 SkRect circle = GrTest::TestSquare(random);
3455 SkPoint center = {circle.centerX(), circle.centerY()};
3456 SkScalar radius = circle.width() / 2.f;
3457 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3458 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3459 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3460 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3461 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003462 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3463 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003464 startAngle, onAngle, offAngle, phase);
3465}
3466
Brian Salomon05441c42017-05-15 16:45:49 -04003467GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003468 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003469 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003470 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003471 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003472}
3473
Brian Salomon05441c42017-05-15 16:45:49 -04003474GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003475 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003476 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003477 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003478 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003479}
3480
Jim Van Verth64b85892019-06-17 12:01:46 -04003481GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3482 do {
3483 SkScalar rotate = random->nextSScalar1() * 360.f;
3484 SkScalar translateX = random->nextSScalar1() * 1000.f;
3485 SkScalar translateY = random->nextSScalar1() * 1000.f;
3486 SkScalar scale;
3487 do {
3488 scale = random->nextSScalar1() * 100.f;
3489 } while (scale == 0);
3490 SkMatrix viewMatrix;
3491 viewMatrix.setRotate(rotate);
3492 viewMatrix.postTranslate(translateX, translateY);
3493 viewMatrix.postScale(scale, scale);
3494 SkRect rect = GrTest::TestRect(random);
3495 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3496 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3497 if (rrect.isOval()) {
3498 continue;
3499 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003500 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003501 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3502 GrTest::TestStrokeRec(random), nullptr);
3503 if (op) {
3504 return op;
3505 }
3506 assert_alive(paint);
3507 } while (true);
3508}
3509
Brian Salomon05441c42017-05-15 16:45:49 -04003510GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003511 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003512 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003513 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003514 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003515}
3516
3517#endif