blob: ea0f41942043d5004f56536fcfdd8aa08c646845 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Robert Phillips06273bc2021-08-11 15:43:50 -04008#include "src/gpu/ops/GrOvalOpFactory.h"
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -040011#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/core/SkRRectPriv.h"
13#include "src/gpu/GrCaps.h"
14#include "src/gpu/GrDrawOpTest.h"
15#include "src/gpu/GrGeometryProcessor.h"
16#include "src/gpu/GrOpFlushState.h"
17#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050018#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/GrResourceProvider.h"
20#include "src/gpu/GrShaderCaps.h"
21#include "src/gpu/GrStyle.h"
22#include "src/gpu/GrVertexWriter.h"
23#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
25#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "src/gpu/glsl/GrGLSLVarying.h"
27#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
28#include "src/gpu/ops/GrMeshDrawOp.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050029#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
Brian Salomon13b28732021-08-06 15:33:58 -040078 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -040079 b->addBool(fStroke, "stroked" );
80 b->addBool(fInClipPlane.isInitialized(), "clipPlane" );
81 b->addBool(fInIsectPlane.isInitialized(), "isectPlane" );
82 b->addBool(fInUnionPlane.isInitialized(), "unionPlane" );
83 b->addBool(fInRoundCapCenters.isInitialized(), "roundCapCenters");
84 b->addBits(ProgramImpl::kMatrixKeyBits,
85 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
86 "localMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050087 }
88
Brian Salomonf95940b2021-08-09 15:56:24 -040089 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -040090 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050091 }
92
93private:
Greg Daniel2655ede2019-04-10 00:49:28 +000094 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050095 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040096 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040097 , fLocalMatrix(localMatrix)
98 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050099 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500100 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500101 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
102
bsalomon4f3a0ca2016-08-22 13:14:26 -0700103 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400104 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700105 }
106 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400107 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700108 }
109 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400110 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700111 }
Brian Salomon45c92202018-04-10 10:53:58 -0400112 if (roundCaps) {
113 SkASSERT(stroke);
114 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400115 fInRoundCapCenters =
116 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400117 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500118 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000119 }
120
Brian Salomonbab2d112021-08-11 09:59:56 -0400121 class Impl : public ProgramImpl {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400123 void setData(const GrGLSLProgramDataManager& pdman,
124 const GrShaderCaps& shaderCaps,
125 const GrGeometryProcessor& geomProc) override {
126 SetTransform(pdman,
127 shaderCaps,
128 fLocalMatrixUniform,
129 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
130 &fLocalMatrix);
131 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000132
Brian Salomonbab2d112021-08-11 09:59:56 -0400133 private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500134 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400135 const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800136 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800137 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800138 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700139 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800140
joshualittabb52a12015-01-13 15:02:10 -0800141 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800142 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400143 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon48959462021-08-11 13:01:06 -0400144 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge.asShaderVar(), "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400145 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400146 fragBuilder->codeAppend("half3 clipPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400147 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane.asShaderVar(),
148 "clipPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700149 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400150 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400151 fragBuilder->codeAppend("half3 isectPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400152 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane.asShaderVar(),
153 "isectPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700154 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400155 if (cgp.fInUnionPlane.isInitialized()) {
156 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400157 fragBuilder->codeAppend("half3 unionPlane;");
Brian Salomon48959462021-08-11 13:01:06 -0400158 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane.asShaderVar(),
159 "unionPlane");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700160 }
Brian Salomon45c92202018-04-10 10:53:58 -0400161 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400162 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400163 fragBuilder->codeAppend("float4 roundCapCenters;");
Brian Salomon48959462021-08-11 13:01:06 -0400164 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters.asShaderVar(),
165 "roundCapCenters");
Brian Salomon45c92202018-04-10 10:53:58 -0400166 varyingHandler->addVarying("capRadius", &capRadius,
167 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
168 // This is the cap radius in normalized space where the outer radius is 1 and
169 // circledEdge.w is the normalized inner radius.
170 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500171 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400172 }
joshualittabb52a12015-01-13 15:02:10 -0800173
joshualittb8c241a2015-05-19 08:23:30 -0700174 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500175 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400176 varyingHandler->addPassThroughAttribute(cgp.fInColor.asShaderVar(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800177
joshualittabb52a12015-01-13 15:02:10 -0800178 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400179 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
180 WriteLocalCoord(vertBuilder,
181 uniformHandler,
182 *args.fShaderCaps,
183 gpArgs,
184 cgp.fInPosition.asShaderVar(),
185 cgp.fLocalMatrix,
186 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800187
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400188 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500189 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000190 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800191 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500192 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000194 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800195 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000197
Brian Salomon92be2f72018-06-19 14:33:47 -0400198 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500199 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000200 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
201 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400202 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500203 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000204 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
205 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700206 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400207 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500208 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000209 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
210 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700211 }
212 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400213 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400214 // We compute coverage of the round caps as circles at the butt caps produced
215 // by the clip planes. The inverse of the clip planes is applied so that there
216 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400217 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500218 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
219 " roundCapCenters.xy)));"
220 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
221 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400222 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400223 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
224 capRadius.fsIn(), capRadius.fsIn());
225 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700226 }
John Stiles4d7ac492021-03-09 20:16:43 -0500227 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 }
229
Michael Ludwig553db622020-06-19 10:47:30 -0400230 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
231 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232 };
233
Brian Salomon289e3d82016-12-14 15:52:56 -0500234 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400235
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500236 Attribute fInPosition;
237 Attribute fInColor;
238 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400239 // Optional attributes.
240 Attribute fInClipPlane;
241 Attribute fInIsectPlane;
242 Attribute fInUnionPlane;
243 Attribute fInRoundCapCenters;
244
Brian Salomon289e3d82016-12-14 15:52:56 -0500245 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400246 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247
John Stiles7571f9e2020-09-02 22:42:33 -0400248 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249};
250
bsalomoncdaa97b2016-03-08 08:30:14 -0800251GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252
Hal Canary6f6961e2017-01-31 13:50:44 -0500253#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500254GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400255 bool stroke = d->fRandom->nextBool();
256 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500257 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400258 bool clipPlane = d->fRandom->nextBool();
259 bool isectPlane = d->fRandom->nextBool();
260 bool unionPlane = d->fRandom->nextBool();
261 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500262 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
263 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264}
Hal Canary6f6961e2017-01-31 13:50:44 -0500265#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400267class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
268public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
270 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600271 return arena->make([&](void* ptr) {
272 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
273 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274 }
275
276 ~ButtCapDashedCircleGeometryProcessor() override {}
277
278 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
279
Brian Salomon13b28732021-08-06 15:33:58 -0400280 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400281 b->addBits(ProgramImpl::kMatrixKeyBits,
282 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
283 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400284 }
285
Brian Salomonf95940b2021-08-09 15:56:24 -0400286 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400287 return std::make_unique<Impl>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400288 }
289
290private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500291 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
292 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
293 , fLocalMatrix(localMatrix) {
294 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
295 fInColor = MakeColorAttribute("inColor", wideColor);
296 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
297 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
298 this->setVertexAttributes(&fInPosition, 4);
299 }
300
Brian Salomonbab2d112021-08-11 09:59:56 -0400301 class Impl : public ProgramImpl {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400302 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400303 void setData(const GrGLSLProgramDataManager& pdman,
304 const GrShaderCaps& shaderCaps,
305 const GrGeometryProcessor& geomProc) override {
306 SetTransform(pdman,
307 shaderCaps,
308 fLocalMatrixUniform,
309 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
310 &fLocalMatrix);
311 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400312
Brian Salomonbab2d112021-08-11 09:59:56 -0400313 private:
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
315 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400316 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400317 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
318 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
319 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
320 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
321
322 // emit attributes
323 varyingHandler->emitAttributes(bcscgp);
324 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon48959462021-08-11 13:01:06 -0400325 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge.asShaderVar(),
326 "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400327
328 fragBuilder->codeAppend("float4 dashParams;");
329 varyingHandler->addPassThroughAttribute(
Brian Salomon48959462021-08-11 13:01:06 -0400330 bcscgp.fInDashParams.asShaderVar(),
331 "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
333 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
334 varyingHandler->addVarying("wrapDashes", &wrapDashes,
335 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
336 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
337 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
338 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500339 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400340 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
341 // x = length of on interval, y = length of on + off.
342 // There are two other parameters in dashParams.zw:
343 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
344 // Each interval has a "corresponding" dash which may be shifted partially or
345 // fully out of its interval by the phase. So there may be up to two "visual"
346 // dashes in an interval.
347 // When computing coverage in an interval we look at three dashes. These are the
348 // "corresponding" dashes from the current, previous, and next intervals. Any of these
349 // may be phase shifted into our interval or even when phase=0 they may be within half a
350 // pixel distance of a pixel center in the interval.
351 // When in the first interval we need to check the dash from the last interval. And
352 // similarly when in the last interval we need to check the dash from the first
353 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
354 // We compute the dash begin/end angles in the vertex shader and apply them in the
355 // fragment shader when we detect we're in the first/last interval.
356 vertBuilder->codeAppend(R"(
357 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
358 // to the fragment shader as a varying.
359 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500360 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400361 // We can happen to be perfectly divisible.
362 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500363 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400364 }
365 // Let 'l' be the last interval before reaching 2 pi.
366 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
367 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
368 // interval.
369 half offset = 0;
370 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500371 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400372 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500373 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400374 }
375 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
376 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
377 // min.
378 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
379
380 // Based on the phase determine whether the -1st, 0th, or 1st interval's
381 // "corresponding" dash appears in the 0th interval and is closest to l.
382 offset = 0;
383 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500384 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400385 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500386 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400387 }
388 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
389 wrapDashes.w = wrapDashes.z + dashParams.x;
390 // The start of the dash we're considering may be clipped by the start of the
391 // circle.
392 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
393 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500394 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
396 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
397 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
398
399 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500400 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400401 varyingHandler->addPassThroughAttribute(
Brian Salomon48959462021-08-11 13:01:06 -0400402 bcscgp.fInColor.asShaderVar(),
403 args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400404 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
405
406 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400407 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
408 WriteLocalCoord(vertBuilder,
409 uniformHandler,
410 *args.fShaderCaps,
411 gpArgs,
412 bcscgp.fInPosition.asShaderVar(),
413 bcscgp.fLocalMatrix,
414 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400415
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 GrShaderVar fnArgs[] = {
417 GrShaderVar("angleToEdge", kFloat_GrSLType),
418 GrShaderVar("diameter", kFloat_GrSLType),
419 };
John Stiles6b58a332020-10-26 17:53:06 -0400420 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
421 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400422 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400423 float linearDist;
424 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
425 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400427 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400428 fragBuilder->codeAppend(R"(
429 float d = length(circleEdge.xy) * circleEdge.z;
430
431 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500432 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400433 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500434 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400435 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 edgeAlpha *= innerAlpha;
437
Ethan Nicholase1f55022019-02-05 17:17:40 -0500438 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400439 angleFromStart = mod(angleFromStart, 6.28318530718);
440 float x = mod(angleFromStart, dashParams.y);
441 // Convert the radial distance from center to pixel into a diameter.
442 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500443 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
444 half(dashParams.w));
445 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
446 half(dashParams.y) + half(dashParams.x) -
447 half(dashParams.w));
448 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
449 half(-dashParams.y) + half(dashParams.x) -
450 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400451 half dashAlpha = 0;
452 )");
453 fragBuilder->codeAppendf(R"(
454 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500455 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400456 currDash.y = min(currDash.y, lastIntervalLength);
457 if (nextDash.x >= lastIntervalLength) {
458 // The next dash is outside the 0..2pi range, throw it away
459 nextDash.xy = half2(1000);
460 } else {
461 // Clip the end of the next dash to the end of the circle
462 nextDash.y = min(nextDash.y, lastIntervalLength);
463 }
464 }
465 )", fnName.c_str(), fnName.c_str());
466 fragBuilder->codeAppendf(R"(
467 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500468 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400469 currDash.x = max(currDash.x, 0);
470 if (prevDash.y <= 0) {
471 // The previous dash is outside the 0..2pi range, throw it away
472 prevDash.xy = half2(1000);
473 } else {
474 // Clip the start previous dash to the start of the circle
475 prevDash.x = max(prevDash.x, 0);
476 }
477 }
478 )", fnName.c_str(), fnName.c_str());
479 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500480 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
481 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
482 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483 dashAlpha = min(dashAlpha, 1);
484 edgeAlpha *= dashAlpha;
485 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
486 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500487 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400488 }
489
Michael Ludwig553db622020-06-19 10:47:30 -0400490 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
491 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400492 };
493
494 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500495 Attribute fInPosition;
496 Attribute fInColor;
497 Attribute fInCircleEdge;
498 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400499
500 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
501
John Stiles7571f9e2020-09-02 22:42:33 -0400502 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400503};
504
505#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500506GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500507 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500509 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400510}
511#endif
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/**
516 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000517 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
518 * in both x and y directions.
519 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000520 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000521 */
522
bsalomoncdaa97b2016-03-08 08:30:14 -0800523class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
526 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600527 return arena->make([&](void* ptr) {
528 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
529 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500530 }
531
532 ~EllipseGeometryProcessor() override {}
533
534 const char* name() const override { return "EllipseGeometryProcessor"; }
535
Brian Salomon13b28732021-08-06 15:33:58 -0400536 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400537 b->addBool(fStroke, "stroked");
538 b->addBits(ProgramImpl::kMatrixKeyBits,
539 ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix),
540 "localMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500541 }
542
Brian Salomonf95940b2021-08-09 15:56:24 -0400543 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400544 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500545 }
546
547private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000548 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400549 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500550 : INHERITED(kEllipseGeometryProcessor_ClassID)
551 , fLocalMatrix(localMatrix)
552 , fStroke(stroke)
553 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500554 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500555 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400556 if (useScale) {
557 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
558 } else {
559 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
560 }
561 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500562 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563 }
564
Brian Salomonbab2d112021-08-11 09:59:56 -0400565 class Impl : public ProgramImpl {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000566 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400567 void setData(const GrGLSLProgramDataManager& pdman,
568 const GrShaderCaps& shaderCaps,
569 const GrGeometryProcessor& geomProc) override {
570 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
571 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
572 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000573
Brian Salomonbab2d112021-08-11 09:59:56 -0400574 private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500575 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400576 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800577 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800578 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800579 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000580
joshualittabb52a12015-01-13 15:02:10 -0800581 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800582 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800583
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400584 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
585 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800586 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800587 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500588 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000589
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400590 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800591 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500592 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800593
Chris Dalton60283612018-02-14 13:38:14 -0700594 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700595 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500596 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400597 varyingHandler->addPassThroughAttribute(egp.fInColor.asShaderVar(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800598
joshualittabb52a12015-01-13 15:02:10 -0800599 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400600 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
601 WriteLocalCoord(vertBuilder,
602 uniformHandler,
603 *args.fShaderCaps,
604 gpArgs,
605 egp.fInPosition.asShaderVar(),
606 egp.fLocalMatrix,
607 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800608
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400609 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
610 // to compute both the edges because we need two separate test equations for
611 // the single offset.
612 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
613 // the distance by the gradient, non-uniformly scaled by the inverse of the
614 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800615
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400616 // On medium precision devices, we scale the denominator of the distance equation
617 // before taking the inverse square root to minimize the chance that we're dividing
618 // by zero, then we scale the result back.
619
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000620 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400621 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400622 if (egp.fStroke) {
623 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
624 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400625 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
626 if (egp.fUseScale) {
627 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
628 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
629 } else {
630 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
631 }
632 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700633
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000634 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 if (args.fShaderCaps->floatIs32Bits()) {
636 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
637 } else {
638 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
639 }
640 if (egp.fUseScale) {
641 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
642 ellipseOffsets.fsIn());
643 } else {
644 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
645 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000646 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000647
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000648 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800649 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400650 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800651 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400652 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400653 if (egp.fUseScale) {
654 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
655 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
656 } else {
657 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
658 }
659 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
660 if (!args.fShaderCaps->floatIs32Bits()) {
661 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
662 }
663 if (egp.fUseScale) {
664 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
665 ellipseOffsets.fsIn());
666 } else {
667 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
668 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000669 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000670 }
671
John Stiles4d7ac492021-03-09 20:16:43 -0500672 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673 }
674
Brian Salomonf95940b2021-08-09 15:56:24 -0400675 using INHERITED = ProgramImpl;
Michael Ludwig553db622020-06-19 10:47:30 -0400676
677 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
678 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679 };
680
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500681 Attribute fInPosition;
682 Attribute fInColor;
683 Attribute fInEllipseOffset;
684 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400685
joshualitte3ababe2015-05-15 07:56:07 -0700686 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000687 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400688 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400690 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691
John Stiles7571f9e2020-09-02 22:42:33 -0400692 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693};
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
Hal Canary6f6961e2017-01-31 13:50:44 -0500697#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500698GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400699 bool stroke = d->fRandom->nextBool();
700 bool wideColor = d->fRandom->nextBool();
701 bool useScale = d->fRandom->nextBool();
702 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
703 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704}
Hal Canary6f6961e2017-01-31 13:50:44 -0500705#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000706
707///////////////////////////////////////////////////////////////////////////////
708
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000709/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000710 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000711 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
712 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
713 * using differentials.
714 *
715 * The result is device-independent and can be used with any affine matrix.
716 */
717
bsalomoncdaa97b2016-03-08 08:30:14 -0800718enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720class DIEllipseGeometryProcessor : public GrGeometryProcessor {
721public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500722 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
723 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600724 return arena->make([&](void* ptr) {
725 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
726 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500727 }
728
729 ~DIEllipseGeometryProcessor() override {}
730
731 const char* name() const override { return "DIEllipseGeometryProcessor"; }
732
Brian Salomon13b28732021-08-06 15:33:58 -0400733 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400734 b->addBits(2, static_cast<uint32_t>(fStyle), "style");
735 b->addBits(ProgramImpl::kMatrixKeyBits,
736 ProgramImpl::ComputeMatrixKey(caps, fViewMatrix),
737 "viewMatrixType");
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500738 }
739
Brian Salomonf95940b2021-08-09 15:56:24 -0400740 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
Brian Salomonbab2d112021-08-11 09:59:56 -0400741 return std::make_unique<Impl>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500742 }
743
744private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000745 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400746 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400747 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400748 , fViewMatrix(viewMatrix)
749 , fUseScale(useScale)
750 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500751 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500752 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400753 if (useScale) {
754 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
755 kFloat3_GrSLType};
756 } else {
757 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
758 kFloat2_GrSLType};
759 }
760 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500761 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762 }
763
Brian Salomonbab2d112021-08-11 09:59:56 -0400764 class Impl : public ProgramImpl {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000765 public:
Brian Salomonbab2d112021-08-11 09:59:56 -0400766 void setData(const GrGLSLProgramDataManager& pdman,
767 const GrShaderCaps& shaderCaps,
768 const GrGeometryProcessor& geomProc) override {
769 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000770
Brian Salomonbab2d112021-08-11 09:59:56 -0400771 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
772 }
773
774 private:
joshualitt465283c2015-09-11 08:19:35 -0700775 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400776 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800777 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800778 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800779 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000780
joshualittabb52a12015-01-13 15:02:10 -0800781 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800782 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800783
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400784 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
785 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800786 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500787 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700788
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400789 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800790 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500791 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800792
Chris Dalton60283612018-02-14 13:38:14 -0700793 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500794 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon48959462021-08-11 13:01:06 -0400795 varyingHandler->addPassThroughAttribute(diegp.fInColor.asShaderVar(),
796 args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800797
joshualittabb52a12015-01-13 15:02:10 -0800798 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400799 WriteOutputPosition(vertBuilder,
800 uniformHandler,
801 *args.fShaderCaps,
802 gpArgs,
803 diegp.fInPosition.name(),
804 diegp.fViewMatrix,
805 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400806 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800807
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000808 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
810 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
811 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
812 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500813 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400814 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
815 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500816 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400817 if (diegp.fUseScale) {
818 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
819 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000820
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400821 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000822 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400823 if (args.fShaderCaps->floatIs32Bits()) {
824 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
825 } else {
826 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
827 }
828 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
829 if (diegp.fUseScale) {
830 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
831 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800832 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000833 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000834 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
835 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000836 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000837 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000838 }
839
840 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800841 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800842 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
843 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400844 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
845 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500846 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400847 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
848 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500849 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400850 if (diegp.fUseScale) {
851 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
852 }
853 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
854 if (!args.fShaderCaps->floatIs32Bits()) {
855 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
856 }
857 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
858 if (diegp.fUseScale) {
859 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
860 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000861 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000862 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000863
John Stiles4d7ac492021-03-09 20:16:43 -0500864 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000865 }
866
Brian Salomonbab2d112021-08-11 09:59:56 -0400867 SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
joshualitt5559ca22015-05-21 15:50:36 -0700868 UniformHandle fViewMatrixUniform;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000869 };
870
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500871 Attribute fInPosition;
872 Attribute fInColor;
873 Attribute fInEllipseOffsets0;
874 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400875
Brian Salomon289e3d82016-12-14 15:52:56 -0500876 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400877 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500878 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400880 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000881
John Stiles7571f9e2020-09-02 22:42:33 -0400882 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000883};
884
bsalomoncdaa97b2016-03-08 08:30:14 -0800885GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886
Hal Canary6f6961e2017-01-31 13:50:44 -0500887#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500888GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400889 bool wideColor = d->fRandom->nextBool();
890 bool useScale = d->fRandom->nextBool();
891 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
892 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
893 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000894}
Hal Canary6f6961e2017-01-31 13:50:44 -0500895#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000896
897///////////////////////////////////////////////////////////////////////////////
898
jvanverth6ca48822016-10-07 06:57:32 -0700899// We have two possible cases for geometry for a circle:
900
901// In the case of a normal fill, we draw geometry for the circle as an octagon.
902static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500903 // enter the octagon
904 // clang-format off
905 0, 1, 8, 1, 2, 8,
906 2, 3, 8, 3, 4, 8,
907 4, 5, 8, 5, 6, 8,
908 6, 7, 8, 7, 0, 8
909 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700910};
911
912// For stroked circles, we use two nested octagons.
913static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 // enter the octagon
915 // clang-format off
916 0, 1, 9, 0, 9, 8,
917 1, 2, 10, 1, 10, 9,
918 2, 3, 11, 2, 11, 10,
919 3, 4, 12, 3, 12, 11,
920 4, 5, 13, 4, 13, 12,
921 5, 6, 14, 5, 14, 13,
922 6, 7, 15, 6, 15, 14,
923 7, 0, 8, 7, 8, 15,
924 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700925};
926
Brian Osman9d958b52018-11-13 12:46:56 -0500927// Normalized geometry for octagons that circumscribe and lie on a circle:
928
929static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
930static constexpr SkPoint kOctagonOuter[] = {
931 SkPoint::Make(-kOctOffset, -1),
932 SkPoint::Make( kOctOffset, -1),
933 SkPoint::Make( 1, -kOctOffset),
934 SkPoint::Make( 1, kOctOffset),
935 SkPoint::Make( kOctOffset, 1),
936 SkPoint::Make(-kOctOffset, 1),
937 SkPoint::Make(-1, kOctOffset),
938 SkPoint::Make(-1, -kOctOffset),
939};
940
941// cosine and sine of pi/8
942static constexpr SkScalar kCosPi8 = 0.923579533f;
943static constexpr SkScalar kSinPi8 = 0.382683432f;
944static constexpr SkPoint kOctagonInner[] = {
945 SkPoint::Make(-kSinPi8, -kCosPi8),
946 SkPoint::Make( kSinPi8, -kCosPi8),
947 SkPoint::Make( kCosPi8, -kSinPi8),
948 SkPoint::Make( kCosPi8, kSinPi8),
949 SkPoint::Make( kSinPi8, kCosPi8),
950 SkPoint::Make(-kSinPi8, kCosPi8),
951 SkPoint::Make(-kCosPi8, kSinPi8),
952 SkPoint::Make(-kCosPi8, -kSinPi8),
953};
Brian Salomon289e3d82016-12-14 15:52:56 -0500954
jvanverth6ca48822016-10-07 06:57:32 -0700955static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
956static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
957static const int kVertsPerStrokeCircle = 16;
958static const int kVertsPerFillCircle = 9;
959
960static int circle_type_to_vert_count(bool stroked) {
961 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
962}
963
964static int circle_type_to_index_count(bool stroked) {
965 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
966}
967
968static const uint16_t* circle_type_to_indices(bool stroked) {
969 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
970}
971
972///////////////////////////////////////////////////////////////////////////////
973
Brian Salomon05441c42017-05-15 16:45:49 -0400974class CircleOp final : public GrMeshDrawOp {
975private:
976 using Helper = GrSimpleMeshDrawOpHelper;
977
joshualitt76e7fb62015-02-11 08:52:27 -0800978public:
Brian Salomon25a88092016-12-01 09:36:50 -0500979 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700980
bsalomon4f3a0ca2016-08-22 13:14:26 -0700981 /** Optional extra params to render a partial arc rather than a full circle. */
982 struct ArcParams {
983 SkScalar fStartAngleRadians;
984 SkScalar fSweepAngleRadians;
985 bool fUseCenter;
986 };
Brian Salomon05441c42017-05-15 16:45:49 -0400987
Herb Derbyc76d4092020-10-07 16:46:15 -0400988 static GrOp::Owner Make(GrRecordingContext* context,
989 GrPaint&& paint,
990 const SkMatrix& viewMatrix,
991 SkPoint center,
992 SkScalar radius,
993 const GrStyle& style,
994 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700995 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700996 if (style.hasPathEffect()) {
997 return nullptr;
998 }
Brian Salomon05441c42017-05-15 16:45:49 -0400999 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001000 SkStrokeRec::Style recStyle = stroke.getStyle();
1001 if (arcParams) {
1002 // Arc support depends on the style.
1003 switch (recStyle) {
1004 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001005 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001006 return nullptr;
1007 case SkStrokeRec::kFill_Style:
1008 // This supports all fills.
1009 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001010 case SkStrokeRec::kStroke_Style:
1011 // Strokes that don't use the center point are supported with butt and round
1012 // caps.
1013 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1014 return nullptr;
1015 }
1016 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001017 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001018 // Hairline only supports butt cap. Round caps could be emulated by slightly
1019 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1021 return nullptr;
1022 }
1023 break;
1024 }
1025 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001026 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1027 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001028 }
1029
Herb Derbyc76d4092020-10-07 16:46:15 -04001030 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001031 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1032 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001033 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001034 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001035 const SkStrokeRec& stroke = style.strokeRec();
1036 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001037
Brian Salomon45c92202018-04-10 10:53:58 -04001038 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001039
bsalomon4b4a7cc2016-07-08 04:42:54 -07001040 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001042 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001043
Brian Salomon289e3d82016-12-14 15:52:56 -05001044 bool isStrokeOnly =
1045 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001046 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001047
jvanverth6ca48822016-10-07 06:57:32 -07001048 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001049 SkScalar outerRadius = radius;
1050 SkScalar halfWidth = 0;
1051 if (hasStroke) {
1052 if (SkScalarNearlyZero(strokeWidth)) {
1053 halfWidth = SK_ScalarHalf;
1054 } else {
1055 halfWidth = SkScalarHalf(strokeWidth);
1056 }
1057
1058 outerRadius += halfWidth;
1059 if (isStrokeOnly) {
1060 innerRadius = radius - halfWidth;
1061 }
1062 }
1063
1064 // The radii are outset for two reasons. First, it allows the shader to simply perform
1065 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1066 // Second, the outer radius is used to compute the verts of the bounding box that is
1067 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001068 outerRadius += SK_ScalarHalf;
1069 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001070 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001071 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001072
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 // This makes every point fully inside the intersection plane.
1074 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1075 // This makes every point fully outside the union plane.
1076 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001077 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1079 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001080 if (arcParams) {
1081 // The shader operates in a space where the circle is translated to be centered at the
1082 // origin. Here we compute points on the unit circle at the starting and ending angles.
1083 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001084 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1085 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001086 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001087 stopPoint.fY = SkScalarSin(endAngle);
1088 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001089
1090 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1091 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1092 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1093 startPoint.normalize();
1094 stopPoint.normalize();
1095
Brian Salomon3517aa72019-12-11 08:16:22 -05001096 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1097 // should orient the clip planes for arcs.
1098 SkASSERT(viewMatrix.isSimilarity());
1099 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1100 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1101 if (upperLeftDet < 0) {
1102 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001103 }
1104
Brian Salomon45c92202018-04-10 10:53:58 -04001105 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1106 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1107 SkPoint roundCaps[2];
1108 if (fRoundCaps) {
1109 // Compute the cap center points in the normalized space.
1110 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1111 roundCaps[0] = startPoint * midRadius;
1112 roundCaps[1] = stopPoint * midRadius;
1113 } else {
1114 roundCaps[0] = kUnusedRoundCaps[0];
1115 roundCaps[1] = kUnusedRoundCaps[1];
1116 }
1117
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001119 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1120 // center of the butts.
1121 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001123 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001124 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001125 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1126 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1127 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 if (useCenter) {
1129 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1130 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001131 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1132 if (arcParams->fSweepAngleRadians < 0) {
1133 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001134 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001135 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001136 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001137 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001138 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001139 color,
1140 innerRadius,
1141 outerRadius,
1142 {norm0.fX, norm0.fY, 0.5f},
1143 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1144 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001145 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001146 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001147 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001148 fClipPlaneIsect = false;
1149 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001150 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001151 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001152 color,
1153 innerRadius,
1154 outerRadius,
1155 {norm0.fX, norm0.fY, 0.5f},
1156 {norm1.fX, norm1.fY, 0.5f},
1157 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001158 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001159 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001160 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001161 fClipPlaneIsect = true;
1162 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001163 }
1164 } else {
1165 // We clip to a secant of the original circle.
1166 startPoint.scale(radius);
1167 stopPoint.scale(radius);
1168 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1169 norm.normalize();
1170 if (arcParams->fSweepAngleRadians > 0) {
1171 norm.negate();
1172 }
1173 SkScalar d = -norm.dot(startPoint) + 0.5f;
1174
Brian Salomon05441c42017-05-15 16:45:49 -04001175 fCircles.emplace_back(
1176 Circle{color,
1177 innerRadius,
1178 outerRadius,
1179 {norm.fX, norm.fY, d},
1180 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1181 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001182 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001183 devBounds,
1184 stroked});
1185 fClipPlane = true;
1186 fClipPlaneIsect = false;
1187 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001188 }
1189 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001190 fCircles.emplace_back(
1191 Circle{color,
1192 innerRadius,
1193 outerRadius,
1194 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1195 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1196 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001197 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001198 devBounds,
1199 stroked});
1200 fClipPlane = false;
1201 fClipPlaneIsect = false;
1202 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001203 }
bsalomon88cf17d2016-07-08 06:40:56 -07001204 // Use the original radius and stroke radius for the bounds so that it does not include the
1205 // AA bloat.
1206 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001207 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001208 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001209 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001210 fVertCount = circle_type_to_vert_count(stroked);
1211 fIndexCount = circle_type_to_index_count(stroked);
1212 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001213 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001214
Brian Salomon289e3d82016-12-14 15:52:56 -05001215 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001216
Robert Phillips294723d2021-06-17 09:23:58 -04001217 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001218 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001219 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001220 } else {
1221 fHelper.visitProxies(func);
1222 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001223 }
1224
Chris Dalton57ab06c2021-04-22 12:57:28 -06001225 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1226 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001227 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001228 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001229 GrProcessorAnalysisCoverage::kSingleChannel, color,
1230 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001231 }
1232
1233 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1234
bsalomone46f9fe2015-08-18 06:05:14 -07001235private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001236 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001237
Robert Phillips4133dc42020-03-11 15:55:55 -04001238 void onCreateProgramInfo(const GrCaps* caps,
1239 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001240 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001241 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001243 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001244 GrXferBarrierFlags renderPassXferBarriers,
1245 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001246 SkASSERT(!usesMSAASurface);
1247
bsalomoncdaa97b2016-03-08 08:30:14 -08001248 SkMatrix localMatrix;
1249 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001250 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001251 }
1252
Robert Phillips4490d922020-03-03 14:50:59 -05001253 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001254 fClipPlaneIsect, fClipPlaneUnion,
1255 fRoundCaps, fWideColor,
1256 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001257
Chris Daltoneb0195e2021-08-18 21:39:02 -06001258 fProgramInfo = fHelper.createProgramInfo(caps,
1259 arena,
1260 writeView,
1261 std::move(appliedClip),
1262 dstProxyView,
1263 gp,
1264 GrPrimitiveType::kTriangles,
1265 renderPassXferBarriers,
1266 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001267 }
1268
Robert Phillips71143952021-06-17 14:55:07 -04001269 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001270 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001271 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001272 if (!fProgramInfo) {
1273 return;
1274 }
1275 }
1276
Brian Salomon12d22642019-01-29 14:38:50 -05001277 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001278 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001279 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001280 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001281 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001282 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001283 return;
1284 }
1285
Brian Salomon12d22642019-01-29 14:38:50 -05001286 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001287 int firstIndex = 0;
1288 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1289 if (!indices) {
1290 SkDebugf("Could not allocate indices\n");
1291 return;
1292 }
1293
1294 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001295 for (const auto& circle : fCircles) {
1296 SkScalar innerRadius = circle.fInnerRadius;
1297 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001298 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001299 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001300
joshualitt76e7fb62015-02-11 08:52:27 -08001301 // The inner radius in the vertex data must be specified in normalized space.
1302 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001303 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001304
1305 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001306 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001307
Brian Osman9a24fee2018-08-03 09:48:42 -04001308 SkVector geoClipPlane = { 0, 0 };
1309 SkScalar offsetClipDist = SK_Scalar1;
1310 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1311 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1312 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1313 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1314 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1315 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1316 // the AA can extend just past the center of the circle.
1317 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1318 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1319 SkAssertResult(geoClipPlane.normalize());
1320 offsetClipDist = 0.5f / halfWidth;
1321 }
1322
Brian Osman7d8f82b2018-11-08 10:24:09 -05001323 for (int i = 0; i < 8; ++i) {
1324 // This clips the normalized offset to the half-plane we computed above. Then we
1325 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001326 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001327 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001328 vertices.write(center + offset * halfWidth,
1329 color,
1330 offset,
1331 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001332 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001334 }
1335 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001336 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001337 }
1338 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001339 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001340 }
1341 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001342 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001343 }
Brian Salomon45c92202018-04-10 10:53:58 -04001344 }
jvanverth6ca48822016-10-07 06:57:32 -07001345
Brian Salomon05441c42017-05-15 16:45:49 -04001346 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001347 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001348
Brian Osman7d8f82b2018-11-08 10:24:09 -05001349 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001350 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1351 color,
1352 kOctagonInner[i] * innerRadius,
1353 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001354 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
1357 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 }
1360 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001361 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001362 }
1363 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001365 }
Brian Salomon45c92202018-04-10 10:53:58 -04001366 }
jvanverth6ca48822016-10-07 06:57:32 -07001367 } else {
1368 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001370 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001372 }
jvanverth6ca48822016-10-07 06:57:32 -07001373 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001374 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001375 }
1376 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001377 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001378 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001379 if (fRoundCaps) {
1380 vertices.write(circle.fRoundCapCenters);
1381 }
jvanverth6ca48822016-10-07 06:57:32 -07001382 }
1383
Brian Salomon05441c42017-05-15 16:45:49 -04001384 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1385 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001386 for (int i = 0; i < primIndexCount; ++i) {
1387 *indices++ = primIndices[i] + currStartVertex;
1388 }
1389
Brian Salomon05441c42017-05-15 16:45:49 -04001390 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001391 }
jvanverth6ca48822016-10-07 06:57:32 -07001392
Robert Phillips4490d922020-03-03 14:50:59 -05001393 fMesh = target->allocMesh();
1394 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001395 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001396 }
1397
1398 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001399 if (!fProgramInfo || !fMesh) {
1400 return;
1401 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001402
Chris Dalton765ed362020-03-16 17:34:44 -06001403 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001404 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001405 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001406 }
1407
Herb Derbye25c3002020-10-27 15:57:27 -04001408 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001409 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001410
1411 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001412 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001413 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001414 }
1415
Brian Salomon05441c42017-05-15 16:45:49 -04001416 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001417 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001418 }
1419
Brian Salomon05441c42017-05-15 16:45:49 -04001420 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001421 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1422 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001423 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001424 }
1425
Brian Salomon289e3d82016-12-14 15:52:56 -05001426 // Because we've set up the ops that don't use the planes with noop values
1427 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001428 fClipPlane |= that->fClipPlane;
1429 fClipPlaneIsect |= that->fClipPlaneIsect;
1430 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001431 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001432 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001433
Brian Salomon05441c42017-05-15 16:45:49 -04001434 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001435 fVertCount += that->fVertCount;
1436 fIndexCount += that->fIndexCount;
1437 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001438 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001439 }
1440
John Stilesaf366522020-08-13 09:57:34 -04001441#if GR_TEST_UTILS
1442 SkString onDumpInfo() const override {
1443 SkString string;
1444 for (int i = 0; i < fCircles.count(); ++i) {
1445 string.appendf(
1446 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1447 "InnerRad: %.2f, OuterRad: %.2f\n",
1448 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1449 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1450 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1451 fCircles[i].fOuterRadius);
1452 }
1453 string += fHelper.dumpInfo();
1454 return string;
1455 }
1456#endif
1457
Brian Salomon05441c42017-05-15 16:45:49 -04001458 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001459 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001460 SkScalar fInnerRadius;
1461 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001462 SkScalar fClipPlane[3];
1463 SkScalar fIsectPlane[3];
1464 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001465 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001466 SkRect fDevBounds;
1467 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001468 };
1469
Brian Salomon289e3d82016-12-14 15:52:56 -05001470 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001471 Helper fHelper;
1472 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001473 int fVertCount;
1474 int fIndexCount;
1475 bool fAllFill;
1476 bool fClipPlane;
1477 bool fClipPlaneIsect;
1478 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001479 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001480 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001481
Chris Daltoneb694b72020-03-16 09:25:50 -06001482 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001483 GrProgramInfo* fProgramInfo = nullptr;
1484
John Stiles7571f9e2020-09-02 22:42:33 -04001485 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001486};
1487
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001488class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1489private:
1490 using Helper = GrSimpleMeshDrawOpHelper;
1491
1492public:
1493 DEFINE_OP_CLASS_ID
1494
Herb Derbyc76d4092020-10-07 16:46:15 -04001495 static GrOp::Owner Make(GrRecordingContext* context,
1496 GrPaint&& paint,
1497 const SkMatrix& viewMatrix,
1498 SkPoint center,
1499 SkScalar radius,
1500 SkScalar strokeWidth,
1501 SkScalar startAngle,
1502 SkScalar onAngle,
1503 SkScalar offAngle,
1504 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001505 SkASSERT(circle_stays_circle(viewMatrix));
1506 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001507 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1508 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001509 onAngle, offAngle, phaseAngle);
1510 }
1511
Herb Derbyc76d4092020-10-07 16:46:15 -04001512 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001513 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1514 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1515 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001516 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001517 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001518 SkASSERT(circle_stays_circle(viewMatrix));
1519 viewMatrix.mapPoints(&center, 1);
1520 radius = viewMatrix.mapRadius(radius);
1521 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1522
1523 // Determine the angle where the circle starts in device space and whether its orientation
1524 // has been reversed.
1525 SkVector start;
1526 bool reflection;
1527 if (!startAngle) {
1528 start = {1, 0};
1529 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001530 start.fY = SkScalarSin(startAngle);
1531 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001532 }
1533 viewMatrix.mapVectors(&start, 1);
1534 startAngle = SkScalarATan2(start.fY, start.fX);
1535 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1536 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1537
1538 auto totalAngle = onAngle + offAngle;
1539 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1540
1541 SkScalar halfWidth = 0;
1542 if (SkScalarNearlyZero(strokeWidth)) {
1543 halfWidth = SK_ScalarHalf;
1544 } else {
1545 halfWidth = SkScalarHalf(strokeWidth);
1546 }
1547
1548 SkScalar outerRadius = radius + halfWidth;
1549 SkScalar innerRadius = radius - halfWidth;
1550
1551 // The radii are outset for two reasons. First, it allows the shader to simply perform
1552 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1553 // Second, the outer radius is used to compute the verts of the bounding box that is
1554 // rendered and the outset ensures the box will cover all partially covered by the circle.
1555 outerRadius += SK_ScalarHalf;
1556 innerRadius -= SK_ScalarHalf;
1557 fViewMatrixIfUsingLocalCoords = viewMatrix;
1558
1559 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1560 center.fX + outerRadius, center.fY + outerRadius);
1561
1562 // We store whether there is a reflection as a negative total angle.
1563 if (reflection) {
1564 totalAngle = -totalAngle;
1565 }
1566 fCircles.push_back(Circle{
1567 color,
1568 outerRadius,
1569 innerRadius,
1570 onAngle,
1571 totalAngle,
1572 startAngle,
1573 phaseAngle,
1574 devBounds
1575 });
1576 // Use the original radius and stroke radius for the bounds so that it does not include the
1577 // AA bloat.
1578 radius += halfWidth;
1579 this->setBounds(
1580 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001581 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001582 fVertCount = circle_type_to_vert_count(true);
1583 fIndexCount = circle_type_to_index_count(true);
1584 }
1585
1586 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1587
Robert Phillips294723d2021-06-17 09:23:58 -04001588 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001589 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001590 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001591 } else {
1592 fHelper.visitProxies(func);
1593 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001594 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001595
Chris Dalton57ab06c2021-04-22 12:57:28 -06001596 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1597 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001598 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001599 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001600 GrProcessorAnalysisCoverage::kSingleChannel, color,
1601 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001602 }
1603
1604 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1605
1606private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001607 GrProgramInfo* programInfo() override { return fProgramInfo; }
1608
Robert Phillips4133dc42020-03-11 15:55:55 -04001609 void onCreateProgramInfo(const GrCaps* caps,
1610 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001611 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001612 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001613 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001614 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001615 GrXferBarrierFlags renderPassXferBarriers,
1616 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001617 SkASSERT(!usesMSAASurface);
1618
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001619 SkMatrix localMatrix;
1620 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001621 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001622 }
1623
1624 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001625 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001626 fWideColor,
1627 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001628
Chris Daltoneb0195e2021-08-18 21:39:02 -06001629 fProgramInfo = fHelper.createProgramInfo(caps,
1630 arena,
1631 writeView,
1632 std::move(appliedClip),
1633 dstProxyView,
1634 gp,
1635 GrPrimitiveType::kTriangles,
1636 renderPassXferBarriers,
1637 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001638 }
1639
Robert Phillips71143952021-06-17 14:55:07 -04001640 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001641 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001642 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001643 if (!fProgramInfo) {
1644 return;
1645 }
1646 }
1647
Brian Salomon12d22642019-01-29 14:38:50 -05001648 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001649 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001650 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001651 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001652 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001653 SkDebugf("Could not allocate vertices\n");
1654 return;
1655 }
1656
Brian Salomon12d22642019-01-29 14:38:50 -05001657 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001658 int firstIndex = 0;
1659 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1660 if (!indices) {
1661 SkDebugf("Could not allocate indices\n");
1662 return;
1663 }
1664
1665 int currStartVertex = 0;
1666 for (const auto& circle : fCircles) {
1667 // The inner radius in the vertex data must be specified in normalized space so that
1668 // length() can be called with smaller values to avoid precision issues with half
1669 // floats.
1670 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1671 const SkRect& bounds = circle.fDevBounds;
1672 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001673 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1674 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1675 };
1676 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001677 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001678 dashParams.totalAngle = -dashParams.totalAngle;
1679 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001680 }
1681
Brian Osmane3caf2d2018-11-21 13:48:36 -05001682 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001683
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001684 // The bounding geometry for the circle is composed of an outer bounding octagon and
1685 // an inner bounded octagon.
1686
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001687 // Compute the vertices of the outer octagon.
1688 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1689 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001690
1691 auto reflectY = [=](const SkPoint& p) {
1692 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001693 };
Brian Osman9d958b52018-11-13 12:46:56 -05001694
1695 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001696 vertices.write(center + kOctagonOuter[i] * halfWidth,
1697 color,
1698 reflectY(kOctagonOuter[i]),
1699 circle.fOuterRadius,
1700 normInnerRadius,
1701 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001702 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001703
1704 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001705 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001706 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1707 color,
1708 reflectY(kOctagonInner[i]) * normInnerRadius,
1709 circle.fOuterRadius,
1710 normInnerRadius,
1711 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001712 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001713
1714 const uint16_t* primIndices = circle_type_to_indices(true);
1715 const int primIndexCount = circle_type_to_index_count(true);
1716 for (int i = 0; i < primIndexCount; ++i) {
1717 *indices++ = primIndices[i] + currStartVertex;
1718 }
1719
1720 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721 }
1722
Robert Phillips4490d922020-03-03 14:50:59 -05001723 fMesh = target->allocMesh();
1724 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001725 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001726 }
1727
1728 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001729 if (!fProgramInfo || !fMesh) {
1730 return;
1731 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001732
Chris Dalton765ed362020-03-16 17:34:44 -06001733 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001734 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001735 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001736 }
1737
Herb Derbye25c3002020-10-27 15:57:27 -04001738 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001739 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1740
1741 // can only represent 65535 unique vertices with 16-bit indices
1742 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001743 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001744 }
1745
1746 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001747 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001748 }
1749
1750 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001751 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1752 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001753 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001754 }
1755
1756 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001757 fVertCount += that->fVertCount;
1758 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001759 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001760 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001761 }
1762
John Stilesaf366522020-08-13 09:57:34 -04001763#if GR_TEST_UTILS
1764 SkString onDumpInfo() const override {
1765 SkString string;
1766 for (int i = 0; i < fCircles.count(); ++i) {
1767 string.appendf(
1768 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1769 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1770 "Phase: %.2f\n",
1771 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1772 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1773 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1774 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1775 fCircles[i].fPhaseAngle);
1776 }
1777 string += fHelper.dumpInfo();
1778 return string;
1779 }
1780#endif
1781
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001782 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001783 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784 SkScalar fOuterRadius;
1785 SkScalar fInnerRadius;
1786 SkScalar fOnAngle;
1787 SkScalar fTotalAngle;
1788 SkScalar fStartAngle;
1789 SkScalar fPhaseAngle;
1790 SkRect fDevBounds;
1791 };
1792
1793 SkMatrix fViewMatrixIfUsingLocalCoords;
1794 Helper fHelper;
1795 SkSTArray<1, Circle, true> fCircles;
1796 int fVertCount;
1797 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001798 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001799
Chris Daltoneb694b72020-03-16 09:25:50 -06001800 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001801 GrProgramInfo* fProgramInfo = nullptr;
1802
John Stiles7571f9e2020-09-02 22:42:33 -04001803 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001804};
1805
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001806///////////////////////////////////////////////////////////////////////////////
1807
Brian Salomon05441c42017-05-15 16:45:49 -04001808class EllipseOp : public GrMeshDrawOp {
1809private:
1810 using Helper = GrSimpleMeshDrawOpHelper;
1811
1812 struct DeviceSpaceParams {
1813 SkPoint fCenter;
1814 SkScalar fXRadius;
1815 SkScalar fYRadius;
1816 SkScalar fInnerXRadius;
1817 SkScalar fInnerYRadius;
1818 };
1819
joshualitt76e7fb62015-02-11 08:52:27 -08001820public:
Brian Salomon25a88092016-12-01 09:36:50 -05001821 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001822
Herb Derbyc76d4092020-10-07 16:46:15 -04001823 static GrOp::Owner Make(GrRecordingContext* context,
1824 GrPaint&& paint,
1825 const SkMatrix& viewMatrix,
1826 const SkRect& ellipse,
1827 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001828 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001829 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001830 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1831 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001832 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1833 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001834 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1835 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1836 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1837 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001838
bsalomon4b4a7cc2016-07-08 04:42:54 -07001839 // do (potentially) anisotropic mapping of stroke
1840 SkVector scaledStroke;
1841 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001842 scaledStroke.fX = SkScalarAbs(
1843 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1844 scaledStroke.fY = SkScalarAbs(
1845 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001846
1847 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001848 bool isStrokeOnly =
1849 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1851
Brian Salomon05441c42017-05-15 16:45:49 -04001852 params.fInnerXRadius = 0;
1853 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001854 if (hasStroke) {
1855 if (SkScalarNearlyZero(scaledStroke.length())) {
1856 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1857 } else {
1858 scaledStroke.scale(SK_ScalarHalf);
1859 }
1860
1861 // we only handle thick strokes for near-circular ellipses
1862 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001863 (0.5f * params.fXRadius > params.fYRadius ||
1864 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001865 return nullptr;
1866 }
1867
1868 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001869 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1870 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1871 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1872 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873 return nullptr;
1874 }
1875
1876 // this is legit only if scale & translation (which should be the case at the moment)
1877 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001878 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1879 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001880 }
1881
Brian Salomon05441c42017-05-15 16:45:49 -04001882 params.fXRadius += scaledStroke.fX;
1883 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001884 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001885
1886 // For large ovals with low precision floats, we fall back to the path renderer.
1887 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1888 // minimum value to avoid divides by zero. With large ovals and low precision this
1889 // leads to blurring at the edge of the oval.
1890 const SkScalar kMaxOvalRadius = 16384;
1891 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1892 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1893 return nullptr;
1894 }
1895
Greg Daniel2655ede2019-04-10 00:49:28 +00001896 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001897 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001898 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001899
Herb Derbyc76d4092020-10-07 16:46:15 -04001900 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001901 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001902 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001903 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001904 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001905 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001906 SkStrokeRec::Style style = stroke.getStyle();
1907 bool isStrokeOnly =
1908 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001909
Brian Salomon05441c42017-05-15 16:45:49 -04001910 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1911 params.fInnerXRadius, params.fInnerYRadius,
1912 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1913 params.fCenter.fY - params.fYRadius,
1914 params.fCenter.fX + params.fXRadius,
1915 params.fCenter.fY + params.fYRadius)});
1916
Greg Daniel5faf4742019-10-01 15:14:44 -04001917 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001918
Brian Salomon05441c42017-05-15 16:45:49 -04001919 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1920 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001921 }
joshualitt76e7fb62015-02-11 08:52:27 -08001922
Brian Salomon289e3d82016-12-14 15:52:56 -05001923 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001924
Robert Phillips294723d2021-06-17 09:23:58 -04001925 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001926 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001927 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001928 } else {
1929 fHelper.visitProxies(func);
1930 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001931 }
1932
Chris Dalton57ab06c2021-04-22 12:57:28 -06001933 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1934 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001935 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1936 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001937 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001938 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001939 GrProcessorAnalysisCoverage::kSingleChannel, color,
1940 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001941 }
1942
1943 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1944
bsalomone46f9fe2015-08-18 06:05:14 -07001945private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001946 GrProgramInfo* programInfo() override { return fProgramInfo; }
1947
Robert Phillips4133dc42020-03-11 15:55:55 -04001948 void onCreateProgramInfo(const GrCaps* caps,
1949 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001950 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001951 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001952 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001953 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001954 GrXferBarrierFlags renderPassXferBarriers,
1955 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001956 SkMatrix localMatrix;
1957 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001958 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001959 }
1960
Robert Phillips4490d922020-03-03 14:50:59 -05001961 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1962 fUseScale, localMatrix);
1963
Chris Daltoneb0195e2021-08-18 21:39:02 -06001964 fProgramInfo = fHelper.createProgramInfo(caps,
1965 arena,
1966 writeView,
1967 std::move(appliedClip),
1968 dstProxyView,
1969 gp,
1970 GrPrimitiveType::kTriangles,
1971 renderPassXferBarriers,
1972 colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001973 }
1974
Robert Phillips71143952021-06-17 14:55:07 -04001975 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001976 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001977 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001978 if (!fProgramInfo) {
1979 return;
1980 }
1981 }
1982
Robert Phillips787fd9d2021-03-22 14:48:09 -04001983 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001984 GrVertexWriter verts{helper.vertices()};
1985 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001986 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001987 return;
1988 }
1989
Chris Dalton25da4062021-07-13 14:06:28 -06001990 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
1991 // full sample coverage.
1992 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
1993
Brian Salomon05441c42017-05-15 16:45:49 -04001994 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001995 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001996 SkScalar xRadius = ellipse.fXRadius;
1997 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001998
1999 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002000 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2001 SkScalarInvert(xRadius),
2002 SkScalarInvert(yRadius),
2003 SkScalarInvert(ellipse.fInnerXRadius),
2004 SkScalarInvert(ellipse.fInnerYRadius)
2005 };
Chris Dalton25da4062021-07-13 14:06:28 -06002006 SkScalar xMaxOffset = xRadius + aaBloat;
2007 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07002008
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002009 if (!fStroked) {
2010 // For filled ellipses we map a unit circle in the vertex attributes rather than
2011 // computing an ellipse and modifying that distance, so we normalize to 1
2012 xMaxOffset /= xRadius;
2013 yMaxOffset /= yRadius;
2014 }
2015
joshualitt76e7fb62015-02-11 08:52:27 -08002016 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002017 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2018 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002019 color,
2020 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002021 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002022 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002023 }
Robert Phillips4490d922020-03-03 14:50:59 -05002024 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002025 }
2026
2027 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002028 if (!fProgramInfo || !fMesh) {
2029 return;
2030 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002031
Chris Dalton765ed362020-03-16 17:34:44 -06002032 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002033 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002034 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002035 }
2036
Herb Derbye25c3002020-10-27 15:57:27 -04002037 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002038 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002039
Brian Salomon05441c42017-05-15 16:45:49 -04002040 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002041 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002042 }
2043
bsalomoncdaa97b2016-03-08 08:30:14 -08002044 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002045 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002046 }
2047
Brian Salomon05441c42017-05-15 16:45:49 -04002048 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002049 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2050 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002051 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002052 }
2053
Brian Salomon05441c42017-05-15 16:45:49 -04002054 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002055 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002056 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002057 }
2058
John Stilesaf366522020-08-13 09:57:34 -04002059#if GR_TEST_UTILS
2060 SkString onDumpInfo() const override {
2061 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2062 for (const auto& geo : fEllipses) {
2063 string.appendf(
2064 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2065 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2066 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2067 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2068 geo.fInnerXRadius, geo.fInnerYRadius);
2069 }
2070 string += fHelper.dumpInfo();
2071 return string;
2072 }
2073#endif
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002076 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002077 SkScalar fXRadius;
2078 SkScalar fYRadius;
2079 SkScalar fInnerXRadius;
2080 SkScalar fInnerYRadius;
2081 SkRect fDevBounds;
2082 };
joshualitt76e7fb62015-02-11 08:52:27 -08002083
Brian Salomon289e3d82016-12-14 15:52:56 -05002084 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002085 Helper fHelper;
2086 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002087 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002088 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002089 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002090
Chris Daltoneb694b72020-03-16 09:25:50 -06002091 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002092 GrProgramInfo* fProgramInfo = nullptr;
2093
John Stiles7571f9e2020-09-02 22:42:33 -04002094 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002095};
2096
joshualitt76e7fb62015-02-11 08:52:27 -08002097/////////////////////////////////////////////////////////////////////////////////////////////////
2098
Brian Salomon05441c42017-05-15 16:45:49 -04002099class DIEllipseOp : public GrMeshDrawOp {
2100private:
2101 using Helper = GrSimpleMeshDrawOpHelper;
2102
2103 struct DeviceSpaceParams {
2104 SkPoint fCenter;
2105 SkScalar fXRadius;
2106 SkScalar fYRadius;
2107 SkScalar fInnerXRadius;
2108 SkScalar fInnerYRadius;
2109 DIEllipseStyle fStyle;
2110 };
2111
joshualitt76e7fb62015-02-11 08:52:27 -08002112public:
Brian Salomon25a88092016-12-01 09:36:50 -05002113 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002114
Herb Derbyc76d4092020-10-07 16:46:15 -04002115 static GrOp::Owner Make(GrRecordingContext* context,
2116 GrPaint&& paint,
2117 const SkMatrix& viewMatrix,
2118 const SkRect& ellipse,
2119 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002120 DeviceSpaceParams params;
2121 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2122 params.fXRadius = SkScalarHalf(ellipse.width());
2123 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002124
bsalomon4b4a7cc2016-07-08 04:42:54 -07002125 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002126 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2127 ? DIEllipseStyle::kStroke
2128 : (SkStrokeRec::kHairline_Style == style)
2129 ? DIEllipseStyle::kHairline
2130 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002131
Brian Salomon05441c42017-05-15 16:45:49 -04002132 params.fInnerXRadius = 0;
2133 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002134 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2135 SkScalar strokeWidth = stroke.getWidth();
2136
2137 if (SkScalarNearlyZero(strokeWidth)) {
2138 strokeWidth = SK_ScalarHalf;
2139 } else {
2140 strokeWidth *= SK_ScalarHalf;
2141 }
2142
2143 // we only handle thick strokes for near-circular ellipses
2144 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002145 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2146 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002147 return nullptr;
2148 }
2149
2150 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002151 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2152 (strokeWidth * strokeWidth) * params.fXRadius) {
2153 return nullptr;
2154 }
2155 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2156 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002157 return nullptr;
2158 }
2159
2160 // set inner radius (if needed)
2161 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002162 params.fInnerXRadius = params.fXRadius - strokeWidth;
2163 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002164 }
2165
Brian Salomon05441c42017-05-15 16:45:49 -04002166 params.fXRadius += strokeWidth;
2167 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002168 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002169
2170 // For large ovals with low precision floats, we fall back to the path renderer.
2171 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2172 // minimum value to avoid divides by zero. With large ovals and low precision this
2173 // leads to blurring at the edge of the oval.
2174 const SkScalar kMaxOvalRadius = 16384;
2175 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2176 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2177 return nullptr;
2178 }
2179
Brian Salomon05441c42017-05-15 16:45:49 -04002180 if (DIEllipseStyle::kStroke == params.fStyle &&
2181 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2182 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002183 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002184 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002185 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002186
Herb Derbyc76d4092020-10-07 16:46:15 -04002187 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002188 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002189 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002190 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002191 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002192 // This expands the outer rect so that after CTM we end up with a half-pixel border
2193 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2194 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2195 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2196 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002197 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2198 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002199
Brian Salomon05441c42017-05-15 16:45:49 -04002200 fEllipses.emplace_back(
2201 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2202 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002203 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2204 params.fCenter.fY - params.fYRadius,
2205 params.fCenter.fX + params.fXRadius,
2206 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002207 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002208 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002209 }
2210
Brian Salomon289e3d82016-12-14 15:52:56 -05002211 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002212
Robert Phillips294723d2021-06-17 09:23:58 -04002213 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002214 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002215 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002216 } else {
2217 fHelper.visitProxies(func);
2218 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002219 }
2220
Chris Dalton57ab06c2021-04-22 12:57:28 -06002221 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2222 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002223 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2224 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002225 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002226 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002227 GrProcessorAnalysisCoverage::kSingleChannel, color,
2228 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002229 }
2230
2231 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2232
bsalomone46f9fe2015-08-18 06:05:14 -07002233private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002234 GrProgramInfo* programInfo() override { return fProgramInfo; }
2235
Robert Phillips4133dc42020-03-11 15:55:55 -04002236 void onCreateProgramInfo(const GrCaps* caps,
2237 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002238 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002239 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002240 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002241 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002242 GrXferBarrierFlags renderPassXferBarriers,
2243 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002244 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2245 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002246 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002247
Chris Daltoneb0195e2021-08-18 21:39:02 -06002248 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
2249 dstProxyView, gp, GrPrimitiveType::kTriangles,
2250 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002251 }
2252
Robert Phillips71143952021-06-17 14:55:07 -04002253 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002254 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002255 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002256 }
2257
Robert Phillips787fd9d2021-03-22 14:48:09 -04002258 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002259 GrVertexWriter verts{helper.vertices()};
2260 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002261 return;
2262 }
2263
Brian Salomon05441c42017-05-15 16:45:49 -04002264 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002265 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002266 SkScalar xRadius = ellipse.fXRadius;
2267 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002268
Chris Dalton25da4062021-07-13 14:06:28 -06002269 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2270 // full sample coverage.
2271 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2272 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2273 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002274
Chris Dalton25da4062021-07-13 14:06:28 -06002275 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2276 // occurs at x^2 + y^2 == 1.
2277 float outerCoordX = drawBounds.width() / (xRadius * 2);
2278 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002279
Chris Dalton25da4062021-07-13 14:06:28 -06002280 // By default, constructed so that inner coord is (0, 0) for all points
2281 float innerCoordX = 0;
2282 float innerCoordY = 0;
2283
2284 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2285 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002286 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002287 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2288 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002289 }
joshualitt76e7fb62015-02-11 08:52:27 -08002290
Chris Dalton25da4062021-07-13 14:06:28 -06002291 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002292 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002293 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002294 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002295 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002296 }
Robert Phillips4490d922020-03-03 14:50:59 -05002297 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002298 }
2299
2300 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002301 if (!fProgramInfo || !fMesh) {
2302 return;
2303 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002304
Chris Dalton765ed362020-03-16 17:34:44 -06002305 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002306 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002307 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002308 }
halcanary9d524f22016-03-29 09:03:52 -07002309
Herb Derbye25c3002020-10-27 15:57:27 -04002310 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002311 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002312 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002313 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002314 }
2315
bsalomoncdaa97b2016-03-08 08:30:14 -08002316 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002317 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002318 }
2319
joshualittd96a67b2015-05-05 14:09:05 -07002320 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002321 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002322 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002323 }
2324
Brian Salomon05441c42017-05-15 16:45:49 -04002325 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002326 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002327 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002328 }
2329
John Stilesaf366522020-08-13 09:57:34 -04002330#if GR_TEST_UTILS
2331 SkString onDumpInfo() const override {
2332 SkString string;
2333 for (const auto& geo : fEllipses) {
2334 string.appendf(
2335 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2336 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2337 "GeoDY: %.2f\n",
2338 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2339 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2340 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2341 }
2342 string += fHelper.dumpInfo();
2343 return string;
2344 }
2345#endif
2346
Brian Salomon05441c42017-05-15 16:45:49 -04002347 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2348 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002349
Brian Salomon05441c42017-05-15 16:45:49 -04002350 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002351 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002352 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002353 SkScalar fXRadius;
2354 SkScalar fYRadius;
2355 SkScalar fInnerXRadius;
2356 SkScalar fInnerYRadius;
2357 SkScalar fGeoDx;
2358 SkScalar fGeoDy;
2359 DIEllipseStyle fStyle;
2360 SkRect fBounds;
2361 };
2362
Brian Salomon05441c42017-05-15 16:45:49 -04002363 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002364 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002365 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002366 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002367
Chris Daltoneb694b72020-03-16 09:25:50 -06002368 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002369 GrProgramInfo* fProgramInfo = nullptr;
2370
John Stiles7571f9e2020-09-02 22:42:33 -04002371 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002372};
2373
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002374///////////////////////////////////////////////////////////////////////////////
2375
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002376// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002377//
2378// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2379// ____________
2380// |_|________|_|
2381// | | | |
2382// | | | |
2383// | | | |
2384// |_|________|_|
2385// |_|________|_|
2386//
2387// For strokes, we don't draw the center quad.
2388//
2389// For circular roundrects, in the case where the stroke width is greater than twice
2390// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002391// in the center. The shared vertices are duplicated so we can set a different outer radius
2392// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002393// ____________
2394// |_|________|_|
2395// | |\ ____ /| |
2396// | | | | | |
2397// | | |____| | |
2398// |_|/______\|_|
2399// |_|________|_|
2400//
2401// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002402//
2403// For filled rrects that need to provide a distance vector we resuse the overstroke
2404// geometry but make the inner rect degenerate (either a point or a horizontal or
2405// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002406
jvanverth84839f62016-08-29 10:16:40 -07002407static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002408 // clang-format off
2409 // overstroke quads
2410 // we place this at the beginning so that we can skip these indices when rendering normally
2411 16, 17, 19, 16, 19, 18,
2412 19, 17, 23, 19, 23, 21,
2413 21, 23, 22, 21, 22, 20,
2414 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002415
Brian Salomon289e3d82016-12-14 15:52:56 -05002416 // corners
2417 0, 1, 5, 0, 5, 4,
2418 2, 3, 7, 2, 7, 6,
2419 8, 9, 13, 8, 13, 12,
2420 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002421
Brian Salomon289e3d82016-12-14 15:52:56 -05002422 // edges
2423 1, 2, 6, 1, 6, 5,
2424 4, 5, 9, 4, 9, 8,
2425 6, 7, 11, 6, 11, 10,
2426 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002427
Brian Salomon289e3d82016-12-14 15:52:56 -05002428 // center
2429 // we place this at the end so that we can ignore these indices when not rendering as filled
2430 5, 6, 10, 5, 10, 9,
2431 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002432};
Brian Salomon289e3d82016-12-14 15:52:56 -05002433
jvanverth84839f62016-08-29 10:16:40 -07002434// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002435static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002436
jvanverth84839f62016-08-29 10:16:40 -07002437// overstroke count is arraysize minus the center indices
2438static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2439// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002440static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002441// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002442static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2443static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002444static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002445
jvanverthc3d0e422016-08-25 08:12:35 -07002446enum RRectType {
2447 kFill_RRectType,
2448 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002449 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002450};
2451
jvanverth84839f62016-08-29 10:16:40 -07002452static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002453 switch (type) {
2454 case kFill_RRectType:
2455 case kStroke_RRectType:
2456 return kVertsPerStandardRRect;
2457 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002458 return kVertsPerOverstrokeRRect;
2459 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002460 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002461}
2462
2463static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002464 switch (type) {
2465 case kFill_RRectType:
2466 return kIndicesPerFillRRect;
2467 case kStroke_RRectType:
2468 return kIndicesPerStrokeRRect;
2469 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002470 return kIndicesPerOverstrokeRRect;
2471 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002472 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002473}
2474
2475static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002476 switch (type) {
2477 case kFill_RRectType:
2478 case kStroke_RRectType:
2479 return gStandardRRectIndices;
2480 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002481 return gOverstrokeRRectIndices;
2482 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002483 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002484}
2485
joshualitt76e7fb62015-02-11 08:52:27 -08002486///////////////////////////////////////////////////////////////////////////////////////////////////
2487
Robert Phillips79839d42016-10-06 15:03:34 -04002488// For distance computations in the interior of filled rrects we:
2489//
2490// add a interior degenerate (point or line) rect
2491// each vertex of that rect gets -outerRad as its radius
2492// this makes the computation of the distance to the outer edge be negative
2493// negative values are caught and then handled differently in the GP's onEmitCode
2494// each vertex is also given the normalized x & y distance from the interior rect's edge
2495// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2496
Brian Salomon05441c42017-05-15 16:45:49 -04002497class CircularRRectOp : public GrMeshDrawOp {
2498private:
2499 using Helper = GrSimpleMeshDrawOpHelper;
2500
joshualitt76e7fb62015-02-11 08:52:27 -08002501public:
Brian Salomon25a88092016-12-01 09:36:50 -05002502 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002503
bsalomon4b4a7cc2016-07-08 04:42:54 -07002504 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2505 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002506 static GrOp::Owner Make(GrRecordingContext* context,
2507 GrPaint&& paint,
2508 const SkMatrix& viewMatrix,
2509 const SkRect& devRect,
2510 float devRadius,
2511 float devStrokeWidth,
2512 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002513 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002514 devRect, devRadius,
2515 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002516 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002517 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002518 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2519 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002520 : INHERITED(ClassID())
2521 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002522 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002523 SkRect bounds = devRect;
2524 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2525 SkScalar innerRadius = 0.0f;
2526 SkScalar outerRadius = devRadius;
2527 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002528 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002529 if (devStrokeWidth > 0) {
2530 if (SkScalarNearlyZero(devStrokeWidth)) {
2531 halfWidth = SK_ScalarHalf;
2532 } else {
2533 halfWidth = SkScalarHalf(devStrokeWidth);
2534 }
joshualitt76e7fb62015-02-11 08:52:27 -08002535
bsalomon4b4a7cc2016-07-08 04:42:54 -07002536 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002537 // Outset stroke by 1/4 pixel
2538 devStrokeWidth += 0.25f;
2539 // If stroke is greater than width or height, this is still a fill
2540 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002541 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002542 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002543 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002544 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002545 }
2546 outerRadius += halfWidth;
2547 bounds.outset(halfWidth, halfWidth);
2548 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002549
Greg Daniel2655ede2019-04-10 00:49:28 +00002550 // The radii are outset for two reasons. First, it allows the shader to simply perform
2551 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2552 // Second, the outer radius is used to compute the verts of the bounding box that is
2553 // rendered and the outset ensures the box will cover all partially covered by the rrect
2554 // corners.
2555 outerRadius += SK_ScalarHalf;
2556 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002557
Greg Daniel5faf4742019-10-01 15:14:44 -04002558 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002559
Greg Daniel2655ede2019-04-10 00:49:28 +00002560 // Expand the rect for aa to generate correct vertices.
2561 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002562
Brian Salomon05441c42017-05-15 16:45:49 -04002563 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002564 fVertCount = rrect_type_to_vert_count(type);
2565 fIndexCount = rrect_type_to_index_count(type);
2566 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002567 }
2568
Brian Salomon289e3d82016-12-14 15:52:56 -05002569 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002570
Robert Phillips294723d2021-06-17 09:23:58 -04002571 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002572 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002573 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002574 } else {
2575 fHelper.visitProxies(func);
2576 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002577 }
2578
Chris Dalton57ab06c2021-04-22 12:57:28 -06002579 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2580 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002581 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002582 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002583 GrProcessorAnalysisCoverage::kSingleChannel, color,
2584 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002585 }
2586
2587 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2588
Brian Salomon92aee3d2016-12-21 09:20:25 -05002589private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002590 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002591 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002592 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002593 SkASSERT(smInset < bigInset);
2594
2595 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002596 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2597 color,
2598 xOffset, 0.0f,
2599 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002600
2601 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002602 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2603 color,
2604 xOffset, 0.0f,
2605 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002606
Brian Osmana1d4eb92018-12-06 16:33:10 -05002607 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2608 color,
2609 0.0f, 0.0f,
2610 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002611
Brian Osmana1d4eb92018-12-06 16:33:10 -05002612 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2613 color,
2614 0.0f, 0.0f,
2615 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002616
Brian Osmana1d4eb92018-12-06 16:33:10 -05002617 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2618 color,
2619 0.0f, 0.0f,
2620 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002621
Brian Osmana1d4eb92018-12-06 16:33:10 -05002622 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2623 color,
2624 0.0f, 0.0f,
2625 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002626
2627 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002628 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2629 color,
2630 xOffset, 0.0f,
2631 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002632
2633 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002634 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2635 color,
2636 xOffset, 0.0f,
2637 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002638 }
2639
Robert Phillips2669a7b2020-03-12 12:07:19 -04002640 GrProgramInfo* programInfo() override { return fProgramInfo; }
2641
Robert Phillips4133dc42020-03-11 15:55:55 -04002642 void onCreateProgramInfo(const GrCaps* caps,
2643 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002644 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002645 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002646 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002647 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002648 GrXferBarrierFlags renderPassXferBarriers,
2649 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002650 SkASSERT(!usesMSAASurface);
2651
bsalomoncdaa97b2016-03-08 08:30:14 -08002652 // Invert the view matrix as a local matrix (if any other processors require coords).
2653 SkMatrix localMatrix;
2654 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002655 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002656 }
2657
Robert Phillips4490d922020-03-03 14:50:59 -05002658 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002659 false, false, false, false,
2660 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002661
Brian Salomon8afde5f2020-04-01 16:22:00 -04002662 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002663 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002664 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002665 }
2666
Robert Phillips71143952021-06-17 14:55:07 -04002667 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002668 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002669 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002670 if (!fProgramInfo) {
2671 return;
2672 }
2673 }
2674
Brian Salomon12d22642019-01-29 14:38:50 -05002675 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002676 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002677
Robert Phillips787fd9d2021-03-22 14:48:09 -04002678 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002679 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002680 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002681 SkDebugf("Could not allocate vertices\n");
2682 return;
2683 }
2684
Brian Salomon12d22642019-01-29 14:38:50 -05002685 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002686 int firstIndex = 0;
2687 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2688 if (!indices) {
2689 SkDebugf("Could not allocate indices\n");
2690 return;
2691 }
2692
2693 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002694 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002695 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002696 SkScalar outerRadius = rrect.fOuterRadius;
2697 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002698
Brian Salomon289e3d82016-12-14 15:52:56 -05002699 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2700 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002701
Brian Salomon289e3d82016-12-14 15:52:56 -05002702 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002703 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002704 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002705 SkScalar innerRadius = rrect.fType != kFill_RRectType
2706 ? rrect.fInnerRadius / rrect.fOuterRadius
2707 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002708 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002709 verts.write(bounds.fLeft, yCoords[i],
2710 color,
2711 -1.0f, yOuterRadii[i],
2712 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002713
Brian Osmana1d4eb92018-12-06 16:33:10 -05002714 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2715 color,
2716 0.0f, yOuterRadii[i],
2717 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002718
Brian Osmana1d4eb92018-12-06 16:33:10 -05002719 verts.write(bounds.fRight - outerRadius, yCoords[i],
2720 color,
2721 0.0f, yOuterRadii[i],
2722 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002723
Brian Osmana1d4eb92018-12-06 16:33:10 -05002724 verts.write(bounds.fRight, yCoords[i],
2725 color,
2726 1.0f, yOuterRadii[i],
2727 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002728 }
jvanverthc3d0e422016-08-25 08:12:35 -07002729 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002730 // Effectively this is an additional stroked rrect, with its
2731 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2732 // This will give us correct AA in the center and the correct
2733 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002734 //
jvanvertha4f1af82016-08-29 07:17:47 -07002735 // Also, the outer offset is a constant vector pointing to the right, which
2736 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002737 if (kOverstroke_RRectType == rrect.fType) {
2738 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002739
Brian Salomon05441c42017-05-15 16:45:49 -04002740 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002741 // this is the normalized distance from the outer rectangle of this
2742 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002743 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002744
Brian Osmana1d4eb92018-12-06 16:33:10 -05002745 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002746 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002747 }
jvanverth6a397612016-08-26 08:15:33 -07002748
Brian Salomon05441c42017-05-15 16:45:49 -04002749 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2750 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002751 for (int i = 0; i < primIndexCount; ++i) {
2752 *indices++ = primIndices[i] + currStartVertex;
2753 }
2754
Brian Salomon05441c42017-05-15 16:45:49 -04002755 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002756 }
2757
Robert Phillips4490d922020-03-03 14:50:59 -05002758 fMesh = target->allocMesh();
2759 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002760 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002761 }
2762
2763 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002764 if (!fProgramInfo || !fMesh) {
2765 return;
2766 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002767
Chris Dalton765ed362020-03-16 17:34:44 -06002768 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002769 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002770 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002771 }
2772
Herb Derbye25c3002020-10-27 15:57:27 -04002773 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002774 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002775
2776 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002777 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002778 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002779 }
2780
Brian Salomon05441c42017-05-15 16:45:49 -04002781 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002782 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002783 }
2784
Brian Salomon05441c42017-05-15 16:45:49 -04002785 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002786 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2787 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002788 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002789 }
2790
Brian Salomon05441c42017-05-15 16:45:49 -04002791 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002792 fVertCount += that->fVertCount;
2793 fIndexCount += that->fIndexCount;
2794 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002795 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002796 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002797 }
2798
John Stilesaf366522020-08-13 09:57:34 -04002799#if GR_TEST_UTILS
2800 SkString onDumpInfo() const override {
2801 SkString string;
2802 for (int i = 0; i < fRRects.count(); ++i) {
2803 string.appendf(
2804 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2805 "InnerRad: %.2f, OuterRad: %.2f\n",
2806 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2807 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2808 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2809 fRRects[i].fOuterRadius);
2810 }
2811 string += fHelper.dumpInfo();
2812 return string;
2813 }
2814#endif
2815
Brian Salomon05441c42017-05-15 16:45:49 -04002816 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002817 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002818 SkScalar fInnerRadius;
2819 SkScalar fOuterRadius;
2820 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002821 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002822 };
2823
Brian Salomon289e3d82016-12-14 15:52:56 -05002824 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002825 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002826 int fVertCount;
2827 int fIndexCount;
2828 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002829 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002830 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002831
Chris Daltoneb694b72020-03-16 09:25:50 -06002832 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002833 GrProgramInfo* fProgramInfo = nullptr;
2834
John Stiles7571f9e2020-09-02 22:42:33 -04002835 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002836};
2837
jvanverth84839f62016-08-29 10:16:40 -07002838static const int kNumRRectsInIndexBuffer = 256;
2839
2840GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2841GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002842static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2843 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002844 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2845 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2846 switch (type) {
2847 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002848 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002849 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2850 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002851 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002852 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002853 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2854 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002855 default:
2856 SkASSERT(false);
2857 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002858 }
jvanverth84839f62016-08-29 10:16:40 -07002859}
2860
Brian Salomon05441c42017-05-15 16:45:49 -04002861class EllipticalRRectOp : public GrMeshDrawOp {
2862private:
2863 using Helper = GrSimpleMeshDrawOpHelper;
2864
joshualitt76e7fb62015-02-11 08:52:27 -08002865public:
Brian Salomon25a88092016-12-01 09:36:50 -05002866 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002867
bsalomon4b4a7cc2016-07-08 04:42:54 -07002868 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2869 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002870 static GrOp::Owner Make(GrRecordingContext* context,
2871 GrPaint&& paint,
2872 const SkMatrix& viewMatrix,
2873 const SkRect& devRect,
2874 float devXRadius,
2875 float devYRadius,
2876 SkVector devStrokeWidths,
2877 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002878 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2879 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002880 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2881 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002882 if (devStrokeWidths.fX > 0) {
2883 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2884 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2885 } else {
2886 devStrokeWidths.scale(SK_ScalarHalf);
2887 }
joshualitt76e7fb62015-02-11 08:52:27 -08002888
bsalomon4b4a7cc2016-07-08 04:42:54 -07002889 // we only handle thick strokes for near-circular ellipses
2890 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002891 (SK_ScalarHalf * devXRadius > devYRadius ||
2892 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 return nullptr;
2894 }
2895
2896 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002897 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2898 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002899 return nullptr;
2900 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002901 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2902 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903 return nullptr;
2904 }
Brian Salomon05441c42017-05-15 16:45:49 -04002905 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002906 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002907 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002908 devXRadius, devYRadius, devStrokeWidths,
2909 strokeOnly);
2910 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002911
Herb Derbyc76d4092020-10-07 16:46:15 -04002912 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002913 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2914 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002915 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002916 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002917 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002918 SkScalar innerXRadius = 0.0f;
2919 SkScalar innerYRadius = 0.0f;
2920 SkRect bounds = devRect;
2921 bool stroked = false;
2922 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002923 // this is legit only if scale & translation (which should be the case at the moment)
2924 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002925 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2926 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002927 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2928 }
2929
Brian Salomon05441c42017-05-15 16:45:49 -04002930 devXRadius += devStrokeHalfWidths.fX;
2931 devYRadius += devStrokeHalfWidths.fY;
2932 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002933 }
2934
Brian Salomon05441c42017-05-15 16:45:49 -04002935 fStroked = stroked;
2936 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002937 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002938 fRRects.emplace_back(
2939 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002940 }
2941
Brian Salomon289e3d82016-12-14 15:52:56 -05002942 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002943
Robert Phillips294723d2021-06-17 09:23:58 -04002944 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002945 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002946 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002947 } else {
2948 fHelper.visitProxies(func);
2949 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002950 }
2951
Chris Dalton57ab06c2021-04-22 12:57:28 -06002952 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2953 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002954 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002955 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002956 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002957 GrProcessorAnalysisCoverage::kSingleChannel, color,
2958 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002959 }
2960
2961 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2962
bsalomone46f9fe2015-08-18 06:05:14 -07002963private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002964 GrProgramInfo* programInfo() override { return fProgramInfo; }
2965
Robert Phillips4133dc42020-03-11 15:55:55 -04002966 void onCreateProgramInfo(const GrCaps* caps,
2967 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002968 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002969 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002970 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002971 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002972 GrXferBarrierFlags renderPassXferBarriers,
2973 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002974 SkMatrix localMatrix;
2975 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002976 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002977 }
2978
Robert Phillips4490d922020-03-03 14:50:59 -05002979 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2980 fUseScale, localMatrix);
2981
Chris Daltoneb0195e2021-08-18 21:39:02 -06002982 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
2983 dstProxyView, gp, GrPrimitiveType::kTriangles,
2984 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002985 }
2986
Robert Phillips71143952021-06-17 14:55:07 -04002987 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002988 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002989 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002990 if (!fProgramInfo) {
2991 return;
2992 }
2993 }
joshualitt76e7fb62015-02-11 08:52:27 -08002994
bsalomonb5238a72015-05-05 07:49:49 -07002995 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002996 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002997 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2998 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002999
Brian Salomon12d22642019-01-29 14:38:50 -05003000 if (!indexBuffer) {
3001 SkDebugf("Could not allocate indices\n");
3002 return;
3003 }
Robert Phillips4490d922020-03-03 14:50:59 -05003004 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003005 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003006 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003007 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003008 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003009 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003010 SkDebugf("Could not allocate vertices\n");
3011 return;
3012 }
3013
Brian Salomon05441c42017-05-15 16:45:49 -04003014 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003015 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003016 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003017 float reciprocalRadii[4] = {
3018 SkScalarInvert(rrect.fXRadius),
3019 SkScalarInvert(rrect.fYRadius),
3020 SkScalarInvert(rrect.fInnerXRadius),
3021 SkScalarInvert(rrect.fInnerYRadius)
3022 };
joshualitt76e7fb62015-02-11 08:52:27 -08003023
Brian Osmane3afdd52020-10-28 10:49:56 -04003024 // If the stroke width is exactly double the radius, the inner radii will be zero.
3025 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3026 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3027 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3028
Chris Dalton25da4062021-07-13 14:06:28 -06003029 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3030 // full sample coverage.
3031 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3032
3033 // Extend out the radii to antialias.
3034 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3035 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003036
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003037 SkScalar xMaxOffset = xOuterRadius;
3038 SkScalar yMaxOffset = yOuterRadius;
3039 if (!fStroked) {
3040 // For filled rrects we map a unit circle in the vertex attributes rather than
3041 // computing an ellipse and modifying that distance, so we normalize to 1.
3042 xMaxOffset /= rrect.fXRadius;
3043 yMaxOffset /= rrect.fYRadius;
3044 }
3045
Chris Dalton25da4062021-07-13 14:06:28 -06003046 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003047
Brian Salomon289e3d82016-12-14 15:52:56 -05003048 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3049 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003050 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003051 SK_ScalarNearlyZero, // we're using inversesqrt() in
3052 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003053 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003054
Brian Osman788b9162020-02-07 10:36:46 -05003055 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003056 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003057 verts.write(bounds.fLeft, yCoords[i],
3058 color,
3059 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003060 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003061 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003062
Brian Osmana1d4eb92018-12-06 16:33:10 -05003063 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3064 color,
3065 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003066 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003067 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003068
Brian Osmana1d4eb92018-12-06 16:33:10 -05003069 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3070 color,
3071 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003072 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003073 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003074
Brian Osmana1d4eb92018-12-06 16:33:10 -05003075 verts.write(bounds.fRight, yCoords[i],
3076 color,
3077 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003078 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003079 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003080 }
3081 }
Robert Phillips4490d922020-03-03 14:50:59 -05003082 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003083 }
3084
3085 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003086 if (!fProgramInfo || !fMesh) {
3087 return;
3088 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003089
Chris Dalton765ed362020-03-16 17:34:44 -06003090 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003091 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003092 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003093 }
3094
Herb Derbye25c3002020-10-27 15:57:27 -04003095 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003096 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003097
Brian Salomon05441c42017-05-15 16:45:49 -04003098 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003099 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003100 }
3101
bsalomoncdaa97b2016-03-08 08:30:14 -08003102 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003103 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003104 }
3105
Brian Salomon05441c42017-05-15 16:45:49 -04003106 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003107 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3108 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003109 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003110 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003111
Brian Salomon05441c42017-05-15 16:45:49 -04003112 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003113 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003114 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003115 }
3116
John Stilesaf366522020-08-13 09:57:34 -04003117#if GR_TEST_UTILS
3118 SkString onDumpInfo() const override {
3119 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3120 for (const auto& geo : fRRects) {
3121 string.appendf(
3122 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3123 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3124 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3125 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3126 geo.fInnerXRadius, geo.fInnerYRadius);
3127 }
3128 string += fHelper.dumpInfo();
3129 return string;
3130 }
3131#endif
3132
Brian Salomon05441c42017-05-15 16:45:49 -04003133 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003134 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003135 SkScalar fXRadius;
3136 SkScalar fYRadius;
3137 SkScalar fInnerXRadius;
3138 SkScalar fInnerYRadius;
3139 SkRect fDevBounds;
3140 };
3141
Brian Salomon289e3d82016-12-14 15:52:56 -05003142 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003143 Helper fHelper;
3144 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003145 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003146 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003147 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003148
Chris Daltoneb694b72020-03-16 09:25:50 -06003149 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003150 GrProgramInfo* fProgramInfo = nullptr;
3151
John Stiles7571f9e2020-09-02 22:42:33 -04003152 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003153};
3154
Herb Derbyc76d4092020-10-07 16:46:15 -04003155GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3156 GrPaint&& paint,
3157 const SkMatrix& viewMatrix,
3158 const SkRRect& rrect,
3159 const SkStrokeRec& stroke,
3160 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003161 SkASSERT(viewMatrix.rectStaysRect());
3162 SkASSERT(viewMatrix.isSimilarity());
3163 SkASSERT(rrect.isSimple());
3164 SkASSERT(!rrect.isOval());
3165 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3166
3167 // RRect ops only handle simple, but not too simple, rrects.
3168 // Do any matrix crunching before we reset the draw state for device coords.
3169 const SkRect& rrectBounds = rrect.getBounds();
3170 SkRect bounds;
3171 viewMatrix.mapRect(&bounds, rrectBounds);
3172
3173 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3174 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3175 viewMatrix[SkMatrix::kMSkewY]));
3176
3177 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3178 SkScalar scaledStroke = -1;
3179 SkScalar strokeWidth = stroke.getWidth();
3180 SkStrokeRec::Style style = stroke.getStyle();
3181
3182 bool isStrokeOnly =
3183 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3184 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3185
3186 if (hasStroke) {
3187 if (SkStrokeRec::kHairline_Style == style) {
3188 scaledStroke = SK_Scalar1;
3189 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003190 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3191 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003192 }
3193 }
3194
3195 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3196 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3197 // patch will have fractional coverage. This only matters when the interior is actually filled.
3198 // We could consider falling back to rect rendering here, since a tiny radius is
3199 // indistinguishable from a square corner.
3200 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3201 return nullptr;
3202 }
3203
3204 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3205 scaledStroke, isStrokeOnly);
3206}
3207
Herb Derbyc76d4092020-10-07 16:46:15 -04003208GrOp::Owner make_rrect_op(GrRecordingContext* context,
3209 GrPaint&& paint,
3210 const SkMatrix& viewMatrix,
3211 const SkRRect& rrect,
3212 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003213 SkASSERT(viewMatrix.rectStaysRect());
3214 SkASSERT(rrect.isSimple());
3215 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003216
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003217 // RRect ops only handle simple, but not too simple, rrects.
3218 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003219 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003220 SkRect bounds;
3221 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003222
Mike Reed242135a2018-02-22 13:41:39 -05003223 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003224 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3225 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3226 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3227 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003228
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003229 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003230
bsalomon4b4a7cc2016-07-08 04:42:54 -07003231 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3232 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003233 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003234
Brian Salomon289e3d82016-12-14 15:52:56 -05003235 bool isStrokeOnly =
3236 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003237 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3238
3239 if (hasStroke) {
3240 if (SkStrokeRec::kHairline_Style == style) {
3241 scaledStroke.set(1, 1);
3242 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003243 scaledStroke.fX = SkScalarAbs(
3244 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3245 scaledStroke.fY = SkScalarAbs(
3246 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003247 }
3248
Jim Van Verth64b85892019-06-17 12:01:46 -04003249 // if half of strokewidth is greater than radius, we don't handle that right now
3250 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3251 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003252 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003253 }
3254 }
3255
Brian Salomon8a97f562019-04-18 14:07:27 -04003256 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003257 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003258 std::swap(xRadius, yRadius);
3259 std::swap(scaledStroke.fX, scaledStroke.fY);
3260 }
3261
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003262 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3263 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3264 // patch will have fractional coverage. This only matters when the interior is actually filled.
3265 // We could consider falling back to rect rendering here, since a tiny radius is
3266 // indistinguishable from a square corner.
3267 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003268 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003269 }
3270
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003271 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003272 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3273 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003274}
3275
Herb Derbyc76d4092020-10-07 16:46:15 -04003276GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3277 GrPaint&& paint,
3278 const SkMatrix& viewMatrix,
3279 const SkRRect& rrect,
3280 const SkStrokeRec& stroke,
3281 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003282 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003283 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003284 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003285 }
3286
3287 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003288 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003289 }
3290
Greg Daniel2655ede2019-04-10 00:49:28 +00003291 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003292}
joshualitt3e708c52015-04-30 13:49:27 -07003293
bsalomon4b4a7cc2016-07-08 04:42:54 -07003294///////////////////////////////////////////////////////////////////////////////
3295
Herb Derbyc76d4092020-10-07 16:46:15 -04003296GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3297 GrPaint&& paint,
3298 const SkMatrix& viewMatrix,
3299 const SkRect& oval,
3300 const GrStyle& style,
3301 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003302 SkScalar width = oval.width();
3303 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3304 circle_stays_circle(viewMatrix));
3305
3306 auto r = width / 2.f;
3307 SkPoint center = { oval.centerX(), oval.centerY() };
3308 if (style.hasNonDashPathEffect()) {
3309 return nullptr;
3310 } else if (style.isDashed()) {
3311 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3312 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3313 return nullptr;
3314 }
3315 auto onInterval = style.dashIntervals()[0];
3316 auto offInterval = style.dashIntervals()[1];
3317 if (offInterval == 0) {
3318 GrStyle strokeStyle(style.strokeRec(), nullptr);
3319 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3320 strokeStyle, shaderCaps);
3321 } else if (onInterval == 0) {
3322 // There is nothing to draw but we have no way to indicate that here.
3323 return nullptr;
3324 }
3325 auto angularOnInterval = onInterval / r;
3326 auto angularOffInterval = offInterval / r;
3327 auto phaseAngle = style.dashPhase() / r;
3328 // Currently this function doesn't accept ovals with different start angles, though
3329 // it could.
3330 static const SkScalar kStartAngle = 0.f;
3331 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3332 style.strokeRec().getWidth(), kStartAngle,
3333 angularOnInterval, angularOffInterval, phaseAngle);
3334 }
3335 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3336}
3337
Herb Derbyc76d4092020-10-07 16:46:15 -04003338GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3339 GrPaint&& paint,
3340 const SkMatrix& viewMatrix,
3341 const SkRect& oval,
3342 const GrStyle& style,
3343 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003344 if (style.pathEffect()) {
3345 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003346 }
3347
Stan Ilieveb868aa2017-02-21 11:06:16 -05003348 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003349 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003350 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003351 }
3352
Stan Ilieveb868aa2017-02-21 11:06:16 -05003353 // Otherwise, if we have shader derivative support, render as device-independent
3354 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003355 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3356 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3357 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3358 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3359 // Check for near-degenerate matrix
3360 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003361 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003362 style.strokeRec());
3363 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003364 }
3365
bsalomon4b4a7cc2016-07-08 04:42:54 -07003366 return nullptr;
3367}
3368
3369///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003370
Herb Derbyc76d4092020-10-07 16:46:15 -04003371GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3372 GrPaint&& paint,
3373 const SkMatrix& viewMatrix,
3374 const SkRect& oval, SkScalar startAngle,
3375 SkScalar sweepAngle, bool useCenter,
3376 const GrStyle& style,
3377 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003378 SkASSERT(!oval.isEmpty());
3379 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003380 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003381 if (SkScalarAbs(sweepAngle) >= 360.f) {
3382 return nullptr;
3383 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003384 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3385 return nullptr;
3386 }
3387 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003388 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3389 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003390 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003391 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392}
3393
3394///////////////////////////////////////////////////////////////////////////////
3395
Hal Canary6f6961e2017-01-31 13:50:44 -05003396#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003397
Brian Salomon05441c42017-05-15 16:45:49 -04003398GR_DRAW_OP_TEST_DEFINE(CircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003399 if (numSamples > 1) {
3400 return nullptr;
3401 }
3402
bsalomon4f3a0ca2016-08-22 13:14:26 -07003403 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003404 SkScalar rotate = random->nextSScalar1() * 360.f;
3405 SkScalar translateX = random->nextSScalar1() * 1000.f;
3406 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003407 SkScalar scale;
3408 do {
3409 scale = random->nextSScalar1() * 100.f;
3410 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003411 SkMatrix viewMatrix;
3412 viewMatrix.setRotate(rotate);
3413 viewMatrix.postTranslate(translateX, translateY);
3414 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003415 SkRect circle = GrTest::TestSquare(random);
3416 SkPoint center = {circle.centerX(), circle.centerY()};
3417 SkScalar radius = circle.width() / 2.f;
3418 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003419 CircleOp::ArcParams arcParamsTmp;
3420 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003421 if (random->nextBool()) {
3422 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003423 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3424 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003425 arcParams = &arcParamsTmp;
3426 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003427 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3428 center, radius,
3429 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003430 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003431 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003432 }
Mike Klein16885072018-12-11 09:54:31 -05003433 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003434 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003435}
3436
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003437GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003438 if (numSamples > 1) {
3439 return nullptr;
3440 }
3441
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003442 SkScalar rotate = random->nextSScalar1() * 360.f;
3443 SkScalar translateX = random->nextSScalar1() * 1000.f;
3444 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003445 SkScalar scale;
3446 do {
3447 scale = random->nextSScalar1() * 100.f;
3448 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003449 SkMatrix viewMatrix;
3450 viewMatrix.setRotate(rotate);
3451 viewMatrix.postTranslate(translateX, translateY);
3452 viewMatrix.postScale(scale, scale);
3453 SkRect circle = GrTest::TestSquare(random);
3454 SkPoint center = {circle.centerX(), circle.centerY()};
3455 SkScalar radius = circle.width() / 2.f;
3456 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3457 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3458 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3459 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3460 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003461 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3462 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003463 startAngle, onAngle, offAngle, phase);
3464}
3465
Brian Salomon05441c42017-05-15 16:45:49 -04003466GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003467 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003468 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003469 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003470 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003471}
3472
Brian Salomon05441c42017-05-15 16:45:49 -04003473GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003474 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003475 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003476 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003477 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003478}
3479
Jim Van Verth64b85892019-06-17 12:01:46 -04003480GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3481 do {
3482 SkScalar rotate = random->nextSScalar1() * 360.f;
3483 SkScalar translateX = random->nextSScalar1() * 1000.f;
3484 SkScalar translateY = random->nextSScalar1() * 1000.f;
3485 SkScalar scale;
3486 do {
3487 scale = random->nextSScalar1() * 100.f;
3488 } while (scale == 0);
3489 SkMatrix viewMatrix;
3490 viewMatrix.setRotate(rotate);
3491 viewMatrix.postTranslate(translateX, translateY);
3492 viewMatrix.postScale(scale, scale);
3493 SkRect rect = GrTest::TestRect(random);
3494 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3495 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3496 if (rrect.isOval()) {
3497 continue;
3498 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003499 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003500 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3501 GrTest::TestStrokeRec(random), nullptr);
3502 if (op) {
3503 return op;
3504 }
3505 assert_alive(paint);
3506 } while (true);
3507}
3508
Brian Salomon05441c42017-05-15 16:45:49 -04003509GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003510 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003511 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003512 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003513 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003514}
3515
3516#endif