blob: 873fe5d2190ef2aa665acf6afb517efae6740c90 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
78 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
79 GLSLProcessor::GenKey(*this, caps, b);
80 }
81
Robert Phillipsf10535f2021-03-23 09:30:45 -040082 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050083 return new GLSLProcessor();
84 }
85
86private:
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400119 const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500155 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500156 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800157
joshualittabb52a12015-01-13 15:02:10 -0800158 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400159 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
160 WriteLocalCoord(vertBuilder,
161 uniformHandler,
162 *args.fShaderCaps,
163 gpArgs,
164 cgp.fInPosition.asShaderVar(),
165 cgp.fLocalMatrix,
166 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800167
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400168 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500169 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000170 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800171 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500172 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500173 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000174 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800175 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000177
Brian Salomon92be2f72018-06-19 14:33:47 -0400178 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500179 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000180 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
181 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
185 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400187 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500188 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000189 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
190 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700191 }
192 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400193 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400194 // We compute coverage of the round caps as circles at the butt caps produced
195 // by the clip planes. The inverse of the clip planes is applied so that there
196 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400197 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500198 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
199 " roundCapCenters.xy)));"
200 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
201 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400202 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400203 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
204 capRadius.fsIn(), capRadius.fsIn());
205 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700206 }
John Stiles4d7ac492021-03-09 20:16:43 -0500207 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208 }
209
robertphillips46d36f02015-01-18 08:14:14 -0800210 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400211 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700212 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800213 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500214 b->addBool(cgp.fStroke, "stroked");
215 b->addBool(cgp.fInClipPlane.isInitialized(), "clipPlane");
216 b->addBool(cgp.fInIsectPlane.isInitialized(), "isectPlane");
217 b->addBool(cgp.fInUnionPlane.isInitialized(), "unionPlane");
218 b->addBool(cgp.fInRoundCapCenters.isInitialized(), "roundCapCenters");
Brian Salomon5a328282021-04-14 10:32:25 -0400219 b->addBits(kMatrixKeyBits,
220 ComputeMatrixKey(shaderCaps, cgp.fLocalMatrix),
221 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222 }
223
Brian Osman609f1592020-07-01 15:14:39 -0400224 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400225 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400226 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400227 SetTransform(pdman,
228 shaderCaps,
229 fLocalMatrixUniform,
230 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
231 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700232 }
233
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400235 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400236
237 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
238 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239 };
240
Brian Salomon289e3d82016-12-14 15:52:56 -0500241 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400242
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500243 Attribute fInPosition;
244 Attribute fInColor;
245 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400246 // Optional attributes.
247 Attribute fInClipPlane;
248 Attribute fInIsectPlane;
249 Attribute fInUnionPlane;
250 Attribute fInRoundCapCenters;
251
Brian Salomon289e3d82016-12-14 15:52:56 -0500252 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400253 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254
John Stiles7571f9e2020-09-02 22:42:33 -0400255 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256};
257
bsalomoncdaa97b2016-03-08 08:30:14 -0800258GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000259
Hal Canary6f6961e2017-01-31 13:50:44 -0500260#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400262 bool stroke = d->fRandom->nextBool();
263 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500264 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400265 bool clipPlane = d->fRandom->nextBool();
266 bool isectPlane = d->fRandom->nextBool();
267 bool unionPlane = d->fRandom->nextBool();
268 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
270 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271}
Hal Canary6f6961e2017-01-31 13:50:44 -0500272#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
275public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500276 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
277 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600278 return arena->make([&](void* ptr) {
279 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
280 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400281 }
282
283 ~ButtCapDashedCircleGeometryProcessor() override {}
284
285 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
286
287 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
288 GLSLProcessor::GenKey(*this, caps, b);
289 }
290
Robert Phillipsf10535f2021-03-23 09:30:45 -0400291 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400292 return new GLSLProcessor();
293 }
294
295private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500296 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
297 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
298 , fLocalMatrix(localMatrix) {
299 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
300 fInColor = MakeColorAttribute("inColor", wideColor);
301 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
302 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
303 this->setVertexAttributes(&fInPosition, 4);
304 }
305
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400306 class GLSLProcessor : public GrGLSLGeometryProcessor {
307 public:
308 GLSLProcessor() {}
309
310 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
311 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400312 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400313 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
314 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
315 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
316 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
317
318 // emit attributes
319 varyingHandler->emitAttributes(bcscgp);
320 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500321 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400322
323 fragBuilder->codeAppend("float4 dashParams;");
324 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
327 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
328 varyingHandler->addVarying("wrapDashes", &wrapDashes,
329 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
330 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
331 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500333 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400334 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
335 // x = length of on interval, y = length of on + off.
336 // There are two other parameters in dashParams.zw:
337 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
338 // Each interval has a "corresponding" dash which may be shifted partially or
339 // fully out of its interval by the phase. So there may be up to two "visual"
340 // dashes in an interval.
341 // When computing coverage in an interval we look at three dashes. These are the
342 // "corresponding" dashes from the current, previous, and next intervals. Any of these
343 // may be phase shifted into our interval or even when phase=0 they may be within half a
344 // pixel distance of a pixel center in the interval.
345 // When in the first interval we need to check the dash from the last interval. And
346 // similarly when in the last interval we need to check the dash from the first
347 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
348 // We compute the dash begin/end angles in the vertex shader and apply them in the
349 // fragment shader when we detect we're in the first/last interval.
350 vertBuilder->codeAppend(R"(
351 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
352 // to the fragment shader as a varying.
353 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 // We can happen to be perfectly divisible.
356 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 }
359 // Let 'l' be the last interval before reaching 2 pi.
360 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
361 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
362 // interval.
363 half offset = 0;
364 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500365 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400366 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500367 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400368 }
369 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
370 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
371 // min.
372 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
373
374 // Based on the phase determine whether the -1st, 0th, or 1st interval's
375 // "corresponding" dash appears in the 0th interval and is closest to l.
376 offset = 0;
377 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500378 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400379 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 }
382 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
383 wrapDashes.w = wrapDashes.z + dashParams.x;
384 // The start of the dash we're considering may be clipped by the start of the
385 // circle.
386 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
387 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500388 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
390 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
391 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
392
393 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500394 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500396 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400397 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
398
399 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400400 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
401 WriteLocalCoord(vertBuilder,
402 uniformHandler,
403 *args.fShaderCaps,
404 gpArgs,
405 bcscgp.fInPosition.asShaderVar(),
406 bcscgp.fLocalMatrix,
407 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400409 GrShaderVar fnArgs[] = {
410 GrShaderVar("angleToEdge", kFloat_GrSLType),
411 GrShaderVar("diameter", kFloat_GrSLType),
412 };
John Stiles6b58a332020-10-26 17:53:06 -0400413 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
414 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400415 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 float linearDist;
417 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
418 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400419 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400420 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 fragBuilder->codeAppend(R"(
422 float d = length(circleEdge.xy) * circleEdge.z;
423
424 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500427 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400428 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400429 edgeAlpha *= innerAlpha;
430
Ethan Nicholase1f55022019-02-05 17:17:40 -0500431 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 angleFromStart = mod(angleFromStart, 6.28318530718);
433 float x = mod(angleFromStart, dashParams.y);
434 // Convert the radial distance from center to pixel into a diameter.
435 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
437 half(dashParams.w));
438 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
439 half(dashParams.y) + half(dashParams.x) -
440 half(dashParams.w));
441 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
442 half(-dashParams.y) + half(dashParams.x) -
443 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400444 half dashAlpha = 0;
445 )");
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.y = min(currDash.y, lastIntervalLength);
450 if (nextDash.x >= lastIntervalLength) {
451 // The next dash is outside the 0..2pi range, throw it away
452 nextDash.xy = half2(1000);
453 } else {
454 // Clip the end of the next dash to the end of the circle
455 nextDash.y = min(nextDash.y, lastIntervalLength);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
460 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400462 currDash.x = max(currDash.x, 0);
463 if (prevDash.y <= 0) {
464 // The previous dash is outside the 0..2pi range, throw it away
465 prevDash.xy = half2(1000);
466 } else {
467 // Clip the start previous dash to the start of the circle
468 prevDash.x = max(prevDash.x, 0);
469 }
470 }
471 )", fnName.c_str(), fnName.c_str());
472 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500473 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
474 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
475 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 dashAlpha = min(dashAlpha, 1);
477 edgeAlpha *= dashAlpha;
478 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
479 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500480 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400481 }
482
483 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400484 const GrShaderCaps& shaderCaps,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 GrProcessorKeyBuilder* b) {
486 const ButtCapDashedCircleGeometryProcessor& bcscgp =
487 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400488 b->addBits(kMatrixKeyBits,
489 ComputeMatrixKey(shaderCaps, bcscgp.fLocalMatrix),
490 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 }
492
Brian Osman609f1592020-07-01 15:14:39 -0400493 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400494 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400495 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400496 SetTransform(pdman,
497 shaderCaps,
498 fLocalMatrixUniform,
499 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
500 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501 }
502
503 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400504 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400505
506 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
507 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 };
509
510 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500511 Attribute fInPosition;
512 Attribute fInColor;
513 Attribute fInCircleEdge;
514 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400515
516 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
517
John Stiles7571f9e2020-09-02 22:42:33 -0400518 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400519};
520
521#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500522GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500523 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400524 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400526}
527#endif
528
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000529///////////////////////////////////////////////////////////////////////////////
530
531/**
532 * 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 +0000533 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
534 * in both x and y directions.
535 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000536 * 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 +0000537 */
538
bsalomoncdaa97b2016-03-08 08:30:14 -0800539class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000540public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500541 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
542 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600543 return arena->make([&](void* ptr) {
544 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
545 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500546 }
547
548 ~EllipseGeometryProcessor() override {}
549
550 const char* name() const override { return "EllipseGeometryProcessor"; }
551
552 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
553 GLSLProcessor::GenKey(*this, caps, b);
554 }
555
Robert Phillipsf10535f2021-03-23 09:30:45 -0400556 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500557 return new GLSLProcessor();
558 }
559
560private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000561 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400562 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500563 : INHERITED(kEllipseGeometryProcessor_ClassID)
564 , fLocalMatrix(localMatrix)
565 , fStroke(stroke)
566 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500567 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500568 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400569 if (useScale) {
570 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
571 } else {
572 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
573 }
574 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500575 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576 }
577
egdaniel57d3b032015-11-13 11:57:27 -0800578 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800580 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581
Brian Salomon289e3d82016-12-14 15:52:56 -0500582 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400583 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800584 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800585 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800586 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000587
joshualittabb52a12015-01-13 15:02:10 -0800588 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800589 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800590
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400591 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
592 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800593 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800594 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500595 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000596
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400597 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800598 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500599 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800600
Chris Dalton60283612018-02-14 13:38:14 -0700601 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700602 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500603 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500604 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800605
joshualittabb52a12015-01-13 15:02:10 -0800606 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400607 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
608 WriteLocalCoord(vertBuilder,
609 uniformHandler,
610 *args.fShaderCaps,
611 gpArgs,
612 egp.fInPosition.asShaderVar(),
613 egp.fLocalMatrix,
614 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800615
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400616 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
617 // to compute both the edges because we need two separate test equations for
618 // the single offset.
619 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
620 // the distance by the gradient, non-uniformly scaled by the inverse of the
621 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800622
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400623 // On medium precision devices, we scale the denominator of the distance equation
624 // before taking the inverse square root to minimize the chance that we're dividing
625 // by zero, then we scale the result back.
626
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000627 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400628 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400629 if (egp.fStroke) {
630 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
631 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400632 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
633 if (egp.fUseScale) {
634 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
635 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
636 } else {
637 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
638 }
639 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700640
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000641 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400642 if (args.fShaderCaps->floatIs32Bits()) {
643 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
644 } else {
645 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
646 }
647 if (egp.fUseScale) {
648 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
649 ellipseOffsets.fsIn());
650 } else {
651 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
652 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000653 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000655 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800656 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400657 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800658 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400659 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400660 if (egp.fUseScale) {
661 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
662 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
663 } else {
664 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
665 }
666 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
667 if (!args.fShaderCaps->floatIs32Bits()) {
668 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
669 }
670 if (egp.fUseScale) {
671 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
672 ellipseOffsets.fsIn());
673 } else {
674 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
675 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000676 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677 }
678
John Stiles4d7ac492021-03-09 20:16:43 -0500679 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 }
681
robertphillips46d36f02015-01-18 08:14:14 -0800682 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400683 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700684 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800685 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500686 b->addBool(egp.fStroke, "stroked");
Brian Salomon5a328282021-04-14 10:32:25 -0400687 b->addBits(kMatrixKeyBits,
688 ComputeMatrixKey(shaderCaps, egp.fLocalMatrix),
689 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690 }
691
Brian Osman609f1592020-07-01 15:14:39 -0400692 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400693 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400694 const GrGeometryProcessor& geomProc) override {
695 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400696 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700697 }
698
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400700 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400701
702 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
703 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704 };
705
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500706 Attribute fInPosition;
707 Attribute fInColor;
708 Attribute fInEllipseOffset;
709 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400710
joshualitte3ababe2015-05-15 07:56:07 -0700711 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000712 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400713 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000714
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400715 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000716
John Stiles7571f9e2020-09-02 22:42:33 -0400717 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000718};
719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000721
Hal Canary6f6961e2017-01-31 13:50:44 -0500722#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500723GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400724 bool stroke = d->fRandom->nextBool();
725 bool wideColor = d->fRandom->nextBool();
726 bool useScale = d->fRandom->nextBool();
727 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
728 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000729}
Hal Canary6f6961e2017-01-31 13:50:44 -0500730#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000731
732///////////////////////////////////////////////////////////////////////////////
733
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000734/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000735 * 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 +0000736 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
737 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
738 * using differentials.
739 *
740 * The result is device-independent and can be used with any affine matrix.
741 */
742
bsalomoncdaa97b2016-03-08 08:30:14 -0800743enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000744
bsalomoncdaa97b2016-03-08 08:30:14 -0800745class DIEllipseGeometryProcessor : public GrGeometryProcessor {
746public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500747 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
748 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600749 return arena->make([&](void* ptr) {
750 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
751 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500752 }
753
754 ~DIEllipseGeometryProcessor() override {}
755
756 const char* name() const override { return "DIEllipseGeometryProcessor"; }
757
758 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
759 GLSLProcessor::GenKey(*this, caps, b);
760 }
761
Robert Phillipsf10535f2021-03-23 09:30:45 -0400762 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500763 return new GLSLProcessor();
764 }
765
766private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000767 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400768 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400769 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400770 , fViewMatrix(viewMatrix)
771 , fUseScale(useScale)
772 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500773 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500774 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400775 if (useScale) {
776 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
777 kFloat3_GrSLType};
778 } else {
779 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
780 kFloat2_GrSLType};
781 }
782 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000784 }
785
egdaniel57d3b032015-11-13 11:57:27 -0800786 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000787 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500788 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000789
joshualitt465283c2015-09-11 08:19:35 -0700790 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400791 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800792 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800793 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800794 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000795
joshualittabb52a12015-01-13 15:02:10 -0800796 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800797 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800798
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
800 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800801 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500802 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700803
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400804 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800805 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500806 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800807
Chris Dalton60283612018-02-14 13:38:14 -0700808 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500809 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500810 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800811
joshualittabb52a12015-01-13 15:02:10 -0800812 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400813 WriteOutputPosition(vertBuilder,
814 uniformHandler,
815 *args.fShaderCaps,
816 gpArgs,
817 diegp.fInPosition.name(),
818 diegp.fViewMatrix,
819 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400820 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800821
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400823 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
824 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
825 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
826 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500827 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400828 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
829 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500830 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400831 if (diegp.fUseScale) {
832 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
833 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000834
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000836 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400837 if (args.fShaderCaps->floatIs32Bits()) {
838 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
839 } else {
840 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
841 }
842 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
843 if (diegp.fUseScale) {
844 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
845 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800846 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000848 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
849 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000851 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000852 }
853
854 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800855 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800856 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
857 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400858 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
859 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500860 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400861 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
862 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500863 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400864 if (diegp.fUseScale) {
865 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
866 }
867 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
868 if (!args.fShaderCaps->floatIs32Bits()) {
869 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
870 }
871 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
872 if (diegp.fUseScale) {
873 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
874 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000875 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000876 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000877
John Stiles4d7ac492021-03-09 20:16:43 -0500878 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879 }
880
robertphillips46d36f02015-01-18 08:14:14 -0800881 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400882 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700883 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800884 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500885 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
Brian Salomon5a328282021-04-14 10:32:25 -0400886 b->addBits(kMatrixKeyBits,
887 ComputeMatrixKey(shaderCaps, diegp.fViewMatrix),
888 "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889 }
890
Brian Osman609f1592020-07-01 15:14:39 -0400891 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400892 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400893 const GrGeometryProcessor& geomProc) override {
894 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700895
Brian Salomon5a328282021-04-14 10:32:25 -0400896 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897 }
898
899 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400900 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700901 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800902
John Stiles7571f9e2020-09-02 22:42:33 -0400903 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000904 };
905
Brian Salomon92be2f72018-06-19 14:33:47 -0400906
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500907 Attribute fInPosition;
908 Attribute fInColor;
909 Attribute fInEllipseOffsets0;
910 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400911
Brian Salomon289e3d82016-12-14 15:52:56 -0500912 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400913 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400916 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000917
John Stiles7571f9e2020-09-02 22:42:33 -0400918 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000919};
920
bsalomoncdaa97b2016-03-08 08:30:14 -0800921GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000922
Hal Canary6f6961e2017-01-31 13:50:44 -0500923#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500924GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400925 bool wideColor = d->fRandom->nextBool();
926 bool useScale = d->fRandom->nextBool();
927 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
928 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
929 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000930}
Hal Canary6f6961e2017-01-31 13:50:44 -0500931#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000932
933///////////////////////////////////////////////////////////////////////////////
934
jvanverth6ca48822016-10-07 06:57:32 -0700935// We have two possible cases for geometry for a circle:
936
937// In the case of a normal fill, we draw geometry for the circle as an octagon.
938static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500939 // enter the octagon
940 // clang-format off
941 0, 1, 8, 1, 2, 8,
942 2, 3, 8, 3, 4, 8,
943 4, 5, 8, 5, 6, 8,
944 6, 7, 8, 7, 0, 8
945 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700946};
947
948// For stroked circles, we use two nested octagons.
949static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500950 // enter the octagon
951 // clang-format off
952 0, 1, 9, 0, 9, 8,
953 1, 2, 10, 1, 10, 9,
954 2, 3, 11, 2, 11, 10,
955 3, 4, 12, 3, 12, 11,
956 4, 5, 13, 4, 13, 12,
957 5, 6, 14, 5, 14, 13,
958 6, 7, 15, 6, 15, 14,
959 7, 0, 8, 7, 8, 15,
960 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700961};
962
Brian Osman9d958b52018-11-13 12:46:56 -0500963// Normalized geometry for octagons that circumscribe and lie on a circle:
964
965static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
966static constexpr SkPoint kOctagonOuter[] = {
967 SkPoint::Make(-kOctOffset, -1),
968 SkPoint::Make( kOctOffset, -1),
969 SkPoint::Make( 1, -kOctOffset),
970 SkPoint::Make( 1, kOctOffset),
971 SkPoint::Make( kOctOffset, 1),
972 SkPoint::Make(-kOctOffset, 1),
973 SkPoint::Make(-1, kOctOffset),
974 SkPoint::Make(-1, -kOctOffset),
975};
976
977// cosine and sine of pi/8
978static constexpr SkScalar kCosPi8 = 0.923579533f;
979static constexpr SkScalar kSinPi8 = 0.382683432f;
980static constexpr SkPoint kOctagonInner[] = {
981 SkPoint::Make(-kSinPi8, -kCosPi8),
982 SkPoint::Make( kSinPi8, -kCosPi8),
983 SkPoint::Make( kCosPi8, -kSinPi8),
984 SkPoint::Make( kCosPi8, kSinPi8),
985 SkPoint::Make( kSinPi8, kCosPi8),
986 SkPoint::Make(-kSinPi8, kCosPi8),
987 SkPoint::Make(-kCosPi8, kSinPi8),
988 SkPoint::Make(-kCosPi8, -kSinPi8),
989};
Brian Salomon289e3d82016-12-14 15:52:56 -0500990
jvanverth6ca48822016-10-07 06:57:32 -0700991static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
992static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
993static const int kVertsPerStrokeCircle = 16;
994static const int kVertsPerFillCircle = 9;
995
996static int circle_type_to_vert_count(bool stroked) {
997 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
998}
999
1000static int circle_type_to_index_count(bool stroked) {
1001 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
1002}
1003
1004static const uint16_t* circle_type_to_indices(bool stroked) {
1005 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1006}
1007
1008///////////////////////////////////////////////////////////////////////////////
1009
Brian Salomon05441c42017-05-15 16:45:49 -04001010class CircleOp final : public GrMeshDrawOp {
1011private:
1012 using Helper = GrSimpleMeshDrawOpHelper;
1013
joshualitt76e7fb62015-02-11 08:52:27 -08001014public:
Brian Salomon25a88092016-12-01 09:36:50 -05001015 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001016
bsalomon4f3a0ca2016-08-22 13:14:26 -07001017 /** Optional extra params to render a partial arc rather than a full circle. */
1018 struct ArcParams {
1019 SkScalar fStartAngleRadians;
1020 SkScalar fSweepAngleRadians;
1021 bool fUseCenter;
1022 };
Brian Salomon05441c42017-05-15 16:45:49 -04001023
Herb Derbyc76d4092020-10-07 16:46:15 -04001024 static GrOp::Owner Make(GrRecordingContext* context,
1025 GrPaint&& paint,
1026 const SkMatrix& viewMatrix,
1027 SkPoint center,
1028 SkScalar radius,
1029 const GrStyle& style,
1030 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001032 if (style.hasPathEffect()) {
1033 return nullptr;
1034 }
Brian Salomon05441c42017-05-15 16:45:49 -04001035 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001036 SkStrokeRec::Style recStyle = stroke.getStyle();
1037 if (arcParams) {
1038 // Arc support depends on the style.
1039 switch (recStyle) {
1040 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001041 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001042 return nullptr;
1043 case SkStrokeRec::kFill_Style:
1044 // This supports all fills.
1045 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001046 case SkStrokeRec::kStroke_Style:
1047 // Strokes that don't use the center point are supported with butt and round
1048 // caps.
1049 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1050 return nullptr;
1051 }
1052 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001053 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001054 // Hairline only supports butt cap. Round caps could be emulated by slightly
1055 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001056 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1057 return nullptr;
1058 }
1059 break;
1060 }
1061 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001062 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1063 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001064 }
1065
Herb Derbyc76d4092020-10-07 16:46:15 -04001066 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001067 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1068 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001069 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001070 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001071 const SkStrokeRec& stroke = style.strokeRec();
1072 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073
Brian Salomon45c92202018-04-10 10:53:58 -04001074 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001075
bsalomon4b4a7cc2016-07-08 04:42:54 -07001076 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001077 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001078 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001079
Brian Salomon289e3d82016-12-14 15:52:56 -05001080 bool isStrokeOnly =
1081 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001082 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001083
jvanverth6ca48822016-10-07 06:57:32 -07001084 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001085 SkScalar outerRadius = radius;
1086 SkScalar halfWidth = 0;
1087 if (hasStroke) {
1088 if (SkScalarNearlyZero(strokeWidth)) {
1089 halfWidth = SK_ScalarHalf;
1090 } else {
1091 halfWidth = SkScalarHalf(strokeWidth);
1092 }
1093
1094 outerRadius += halfWidth;
1095 if (isStrokeOnly) {
1096 innerRadius = radius - halfWidth;
1097 }
1098 }
1099
1100 // The radii are outset for two reasons. First, it allows the shader to simply perform
1101 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1102 // Second, the outer radius is used to compute the verts of the bounding box that is
1103 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001104 outerRadius += SK_ScalarHalf;
1105 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001106 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001107 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001108
bsalomon4f3a0ca2016-08-22 13:14:26 -07001109 // This makes every point fully inside the intersection plane.
1110 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1111 // This makes every point fully outside the union plane.
1112 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001113 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001114 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1115 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001116 if (arcParams) {
1117 // The shader operates in a space where the circle is translated to be centered at the
1118 // origin. Here we compute points on the unit circle at the starting and ending angles.
1119 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001120 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1121 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001123 stopPoint.fY = SkScalarSin(endAngle);
1124 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001125
1126 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1127 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1128 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1129 startPoint.normalize();
1130 stopPoint.normalize();
1131
Brian Salomon3517aa72019-12-11 08:16:22 -05001132 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1133 // should orient the clip planes for arcs.
1134 SkASSERT(viewMatrix.isSimilarity());
1135 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1136 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1137 if (upperLeftDet < 0) {
1138 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001139 }
1140
Brian Salomon45c92202018-04-10 10:53:58 -04001141 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1142 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1143 SkPoint roundCaps[2];
1144 if (fRoundCaps) {
1145 // Compute the cap center points in the normalized space.
1146 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1147 roundCaps[0] = startPoint * midRadius;
1148 roundCaps[1] = stopPoint * midRadius;
1149 } else {
1150 roundCaps[0] = kUnusedRoundCaps[0];
1151 roundCaps[1] = kUnusedRoundCaps[1];
1152 }
1153
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001155 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1156 // center of the butts.
1157 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001158 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001159 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001160 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001161 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1162 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1163 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001164 if (useCenter) {
1165 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1166 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001167 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1168 if (arcParams->fSweepAngleRadians < 0) {
1169 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001170 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001171 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001172 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001173 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001174 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001175 color,
1176 innerRadius,
1177 outerRadius,
1178 {norm0.fX, norm0.fY, 0.5f},
1179 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1180 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001181 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001182 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001183 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001184 fClipPlaneIsect = false;
1185 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001186 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001187 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001188 color,
1189 innerRadius,
1190 outerRadius,
1191 {norm0.fX, norm0.fY, 0.5f},
1192 {norm1.fX, norm1.fY, 0.5f},
1193 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001194 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001195 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001196 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001197 fClipPlaneIsect = true;
1198 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001199 }
1200 } else {
1201 // We clip to a secant of the original circle.
1202 startPoint.scale(radius);
1203 stopPoint.scale(radius);
1204 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1205 norm.normalize();
1206 if (arcParams->fSweepAngleRadians > 0) {
1207 norm.negate();
1208 }
1209 SkScalar d = -norm.dot(startPoint) + 0.5f;
1210
Brian Salomon05441c42017-05-15 16:45:49 -04001211 fCircles.emplace_back(
1212 Circle{color,
1213 innerRadius,
1214 outerRadius,
1215 {norm.fX, norm.fY, d},
1216 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1217 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001218 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001219 devBounds,
1220 stroked});
1221 fClipPlane = true;
1222 fClipPlaneIsect = false;
1223 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001224 }
1225 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001226 fCircles.emplace_back(
1227 Circle{color,
1228 innerRadius,
1229 outerRadius,
1230 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1231 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1232 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001233 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001234 devBounds,
1235 stroked});
1236 fClipPlane = false;
1237 fClipPlaneIsect = false;
1238 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001239 }
bsalomon88cf17d2016-07-08 06:40:56 -07001240 // Use the original radius and stroke radius for the bounds so that it does not include the
1241 // AA bloat.
1242 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001243 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001244 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001245 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001246 fVertCount = circle_type_to_vert_count(stroked);
1247 fIndexCount = circle_type_to_index_count(stroked);
1248 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001250
Brian Salomon289e3d82016-12-14 15:52:56 -05001251 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001252
Robert Phillips294723d2021-06-17 09:23:58 -04001253 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001254 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001255 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001256 } else {
1257 fHelper.visitProxies(func);
1258 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001259 }
1260
Chris Dalton57ab06c2021-04-22 12:57:28 -06001261 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1262 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001263 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001264 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001265 GrProcessorAnalysisCoverage::kSingleChannel, color,
1266 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001267 }
1268
1269 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1270
bsalomone46f9fe2015-08-18 06:05:14 -07001271private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001272 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001273
Robert Phillips4133dc42020-03-11 15:55:55 -04001274 void onCreateProgramInfo(const GrCaps* caps,
1275 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001276 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001277 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001278 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001279 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001280 GrXferBarrierFlags renderPassXferBarriers,
1281 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001282 SkASSERT(!usesMSAASurface);
1283
bsalomoncdaa97b2016-03-08 08:30:14 -08001284 SkMatrix localMatrix;
1285 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001286 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001287 }
1288
Robert Phillips4490d922020-03-03 14:50:59 -05001289 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001290 fClipPlaneIsect, fClipPlaneUnion,
1291 fRoundCaps, fWideColor,
1292 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001293
Brian Salomon8afde5f2020-04-01 16:22:00 -04001294 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001295 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001296 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001297 }
1298
Robert Phillips71143952021-06-17 14:55:07 -04001299 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001300 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001301 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001302 if (!fProgramInfo) {
1303 return;
1304 }
1305 }
1306
Brian Salomon12d22642019-01-29 14:38:50 -05001307 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001308 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001309 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001310 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001311 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001312 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001313 return;
1314 }
1315
Brian Salomon12d22642019-01-29 14:38:50 -05001316 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001317 int firstIndex = 0;
1318 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1319 if (!indices) {
1320 SkDebugf("Could not allocate indices\n");
1321 return;
1322 }
1323
1324 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001325 for (const auto& circle : fCircles) {
1326 SkScalar innerRadius = circle.fInnerRadius;
1327 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001328 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001329 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001330
joshualitt76e7fb62015-02-11 08:52:27 -08001331 // The inner radius in the vertex data must be specified in normalized space.
1332 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001334
1335 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001336 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001337
Brian Osman9a24fee2018-08-03 09:48:42 -04001338 SkVector geoClipPlane = { 0, 0 };
1339 SkScalar offsetClipDist = SK_Scalar1;
1340 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1341 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1342 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1343 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1344 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1345 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1346 // the AA can extend just past the center of the circle.
1347 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1348 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1349 SkAssertResult(geoClipPlane.normalize());
1350 offsetClipDist = 0.5f / halfWidth;
1351 }
1352
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 for (int i = 0; i < 8; ++i) {
1354 // This clips the normalized offset to the half-plane we computed above. Then we
1355 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001356 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001357 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(center + offset * halfWidth,
1359 color,
1360 offset,
1361 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001362 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001364 }
1365 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001366 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001367 }
1368 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001370 }
1371 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001372 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001373 }
Brian Salomon45c92202018-04-10 10:53:58 -04001374 }
jvanverth6ca48822016-10-07 06:57:32 -07001375
Brian Salomon05441c42017-05-15 16:45:49 -04001376 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001377 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001378
Brian Osman7d8f82b2018-11-08 10:24:09 -05001379 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001380 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1381 color,
1382 kOctagonInner[i] * innerRadius,
1383 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001384 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001385 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001386 }
1387 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001388 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001389 }
1390 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001391 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001392 }
1393 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001394 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001395 }
Brian Salomon45c92202018-04-10 10:53:58 -04001396 }
jvanverth6ca48822016-10-07 06:57:32 -07001397 } else {
1398 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001399 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001400 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001401 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001402 }
jvanverth6ca48822016-10-07 06:57:32 -07001403 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001404 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001405 }
1406 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001407 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001408 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001409 if (fRoundCaps) {
1410 vertices.write(circle.fRoundCapCenters);
1411 }
jvanverth6ca48822016-10-07 06:57:32 -07001412 }
1413
Brian Salomon05441c42017-05-15 16:45:49 -04001414 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1415 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001416 for (int i = 0; i < primIndexCount; ++i) {
1417 *indices++ = primIndices[i] + currStartVertex;
1418 }
1419
Brian Salomon05441c42017-05-15 16:45:49 -04001420 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001421 }
jvanverth6ca48822016-10-07 06:57:32 -07001422
Robert Phillips4490d922020-03-03 14:50:59 -05001423 fMesh = target->allocMesh();
1424 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001425 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001426 }
1427
1428 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001429 if (!fProgramInfo || !fMesh) {
1430 return;
1431 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001432
Chris Dalton765ed362020-03-16 17:34:44 -06001433 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001434 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001435 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001436 }
1437
Herb Derbye25c3002020-10-27 15:57:27 -04001438 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001439 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001440
1441 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001442 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001443 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001444 }
1445
Brian Salomon05441c42017-05-15 16:45:49 -04001446 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001447 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001448 }
1449
Brian Salomon05441c42017-05-15 16:45:49 -04001450 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001451 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1452 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001453 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001454 }
1455
Brian Salomon289e3d82016-12-14 15:52:56 -05001456 // Because we've set up the ops that don't use the planes with noop values
1457 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001458 fClipPlane |= that->fClipPlane;
1459 fClipPlaneIsect |= that->fClipPlaneIsect;
1460 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001461 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001462 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001463
Brian Salomon05441c42017-05-15 16:45:49 -04001464 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001465 fVertCount += that->fVertCount;
1466 fIndexCount += that->fIndexCount;
1467 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001468 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001469 }
1470
John Stilesaf366522020-08-13 09:57:34 -04001471#if GR_TEST_UTILS
1472 SkString onDumpInfo() const override {
1473 SkString string;
1474 for (int i = 0; i < fCircles.count(); ++i) {
1475 string.appendf(
1476 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1477 "InnerRad: %.2f, OuterRad: %.2f\n",
1478 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1479 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1480 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1481 fCircles[i].fOuterRadius);
1482 }
1483 string += fHelper.dumpInfo();
1484 return string;
1485 }
1486#endif
1487
Brian Salomon05441c42017-05-15 16:45:49 -04001488 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001489 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001490 SkScalar fInnerRadius;
1491 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001492 SkScalar fClipPlane[3];
1493 SkScalar fIsectPlane[3];
1494 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001495 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001496 SkRect fDevBounds;
1497 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001498 };
1499
Brian Salomon289e3d82016-12-14 15:52:56 -05001500 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001501 Helper fHelper;
1502 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001503 int fVertCount;
1504 int fIndexCount;
1505 bool fAllFill;
1506 bool fClipPlane;
1507 bool fClipPlaneIsect;
1508 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001509 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001510 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001511
Chris Daltoneb694b72020-03-16 09:25:50 -06001512 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001513 GrProgramInfo* fProgramInfo = nullptr;
1514
John Stiles7571f9e2020-09-02 22:42:33 -04001515 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001516};
1517
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001518class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1519private:
1520 using Helper = GrSimpleMeshDrawOpHelper;
1521
1522public:
1523 DEFINE_OP_CLASS_ID
1524
Herb Derbyc76d4092020-10-07 16:46:15 -04001525 static GrOp::Owner Make(GrRecordingContext* context,
1526 GrPaint&& paint,
1527 const SkMatrix& viewMatrix,
1528 SkPoint center,
1529 SkScalar radius,
1530 SkScalar strokeWidth,
1531 SkScalar startAngle,
1532 SkScalar onAngle,
1533 SkScalar offAngle,
1534 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001535 SkASSERT(circle_stays_circle(viewMatrix));
1536 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001537 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1538 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001539 onAngle, offAngle, phaseAngle);
1540 }
1541
Herb Derbyc76d4092020-10-07 16:46:15 -04001542 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001543 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1544 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1545 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001546 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001547 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001548 SkASSERT(circle_stays_circle(viewMatrix));
1549 viewMatrix.mapPoints(&center, 1);
1550 radius = viewMatrix.mapRadius(radius);
1551 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1552
1553 // Determine the angle where the circle starts in device space and whether its orientation
1554 // has been reversed.
1555 SkVector start;
1556 bool reflection;
1557 if (!startAngle) {
1558 start = {1, 0};
1559 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001560 start.fY = SkScalarSin(startAngle);
1561 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001562 }
1563 viewMatrix.mapVectors(&start, 1);
1564 startAngle = SkScalarATan2(start.fY, start.fX);
1565 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1566 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1567
1568 auto totalAngle = onAngle + offAngle;
1569 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1570
1571 SkScalar halfWidth = 0;
1572 if (SkScalarNearlyZero(strokeWidth)) {
1573 halfWidth = SK_ScalarHalf;
1574 } else {
1575 halfWidth = SkScalarHalf(strokeWidth);
1576 }
1577
1578 SkScalar outerRadius = radius + halfWidth;
1579 SkScalar innerRadius = radius - halfWidth;
1580
1581 // The radii are outset for two reasons. First, it allows the shader to simply perform
1582 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1583 // Second, the outer radius is used to compute the verts of the bounding box that is
1584 // rendered and the outset ensures the box will cover all partially covered by the circle.
1585 outerRadius += SK_ScalarHalf;
1586 innerRadius -= SK_ScalarHalf;
1587 fViewMatrixIfUsingLocalCoords = viewMatrix;
1588
1589 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1590 center.fX + outerRadius, center.fY + outerRadius);
1591
1592 // We store whether there is a reflection as a negative total angle.
1593 if (reflection) {
1594 totalAngle = -totalAngle;
1595 }
1596 fCircles.push_back(Circle{
1597 color,
1598 outerRadius,
1599 innerRadius,
1600 onAngle,
1601 totalAngle,
1602 startAngle,
1603 phaseAngle,
1604 devBounds
1605 });
1606 // Use the original radius and stroke radius for the bounds so that it does not include the
1607 // AA bloat.
1608 radius += halfWidth;
1609 this->setBounds(
1610 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001611 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001612 fVertCount = circle_type_to_vert_count(true);
1613 fIndexCount = circle_type_to_index_count(true);
1614 }
1615
1616 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1617
Robert Phillips294723d2021-06-17 09:23:58 -04001618 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001619 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001620 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001621 } else {
1622 fHelper.visitProxies(func);
1623 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001624 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001625
Chris Dalton57ab06c2021-04-22 12:57:28 -06001626 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1627 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001628 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001629 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001630 GrProcessorAnalysisCoverage::kSingleChannel, color,
1631 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001632 }
1633
1634 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1635
1636private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001637 GrProgramInfo* programInfo() override { return fProgramInfo; }
1638
Robert Phillips4133dc42020-03-11 15:55:55 -04001639 void onCreateProgramInfo(const GrCaps* caps,
1640 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001641 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001642 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001643 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001644 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001645 GrXferBarrierFlags renderPassXferBarriers,
1646 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001647 SkASSERT(!usesMSAASurface);
1648
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001649 SkMatrix localMatrix;
1650 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001651 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001652 }
1653
1654 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001655 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001656 fWideColor,
1657 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001658
Brian Salomon8afde5f2020-04-01 16:22:00 -04001659 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001660 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001661 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001662 }
1663
Robert Phillips71143952021-06-17 14:55:07 -04001664 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001665 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001666 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001667 if (!fProgramInfo) {
1668 return;
1669 }
1670 }
1671
Brian Salomon12d22642019-01-29 14:38:50 -05001672 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001673 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001674 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001675 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001676 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001677 SkDebugf("Could not allocate vertices\n");
1678 return;
1679 }
1680
Brian Salomon12d22642019-01-29 14:38:50 -05001681 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001682 int firstIndex = 0;
1683 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1684 if (!indices) {
1685 SkDebugf("Could not allocate indices\n");
1686 return;
1687 }
1688
1689 int currStartVertex = 0;
1690 for (const auto& circle : fCircles) {
1691 // The inner radius in the vertex data must be specified in normalized space so that
1692 // length() can be called with smaller values to avoid precision issues with half
1693 // floats.
1694 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1695 const SkRect& bounds = circle.fDevBounds;
1696 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001697 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1698 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1699 };
1700 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001701 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001702 dashParams.totalAngle = -dashParams.totalAngle;
1703 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001704 }
1705
Brian Osmane3caf2d2018-11-21 13:48:36 -05001706 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001707
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001708 // The bounding geometry for the circle is composed of an outer bounding octagon and
1709 // an inner bounded octagon.
1710
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001711 // Compute the vertices of the outer octagon.
1712 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1713 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001714
1715 auto reflectY = [=](const SkPoint& p) {
1716 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001717 };
Brian Osman9d958b52018-11-13 12:46:56 -05001718
1719 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001720 vertices.write(center + kOctagonOuter[i] * halfWidth,
1721 color,
1722 reflectY(kOctagonOuter[i]),
1723 circle.fOuterRadius,
1724 normInnerRadius,
1725 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001726 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001727
1728 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001729 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001730 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1731 color,
1732 reflectY(kOctagonInner[i]) * normInnerRadius,
1733 circle.fOuterRadius,
1734 normInnerRadius,
1735 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001736 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001737
1738 const uint16_t* primIndices = circle_type_to_indices(true);
1739 const int primIndexCount = circle_type_to_index_count(true);
1740 for (int i = 0; i < primIndexCount; ++i) {
1741 *indices++ = primIndices[i] + currStartVertex;
1742 }
1743
1744 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001745 }
1746
Robert Phillips4490d922020-03-03 14:50:59 -05001747 fMesh = target->allocMesh();
1748 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001749 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001750 }
1751
1752 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001753 if (!fProgramInfo || !fMesh) {
1754 return;
1755 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001756
Chris Dalton765ed362020-03-16 17:34:44 -06001757 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001758 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001759 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001760 }
1761
Herb Derbye25c3002020-10-27 15:57:27 -04001762 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001763 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1764
1765 // can only represent 65535 unique vertices with 16-bit indices
1766 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001767 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001768 }
1769
1770 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001771 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001772 }
1773
1774 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001775 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1776 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001777 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001778 }
1779
1780 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001781 fVertCount += that->fVertCount;
1782 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001783 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001784 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001785 }
1786
John Stilesaf366522020-08-13 09:57:34 -04001787#if GR_TEST_UTILS
1788 SkString onDumpInfo() const override {
1789 SkString string;
1790 for (int i = 0; i < fCircles.count(); ++i) {
1791 string.appendf(
1792 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1793 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1794 "Phase: %.2f\n",
1795 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1796 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1797 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1798 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1799 fCircles[i].fPhaseAngle);
1800 }
1801 string += fHelper.dumpInfo();
1802 return string;
1803 }
1804#endif
1805
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001806 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001807 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001808 SkScalar fOuterRadius;
1809 SkScalar fInnerRadius;
1810 SkScalar fOnAngle;
1811 SkScalar fTotalAngle;
1812 SkScalar fStartAngle;
1813 SkScalar fPhaseAngle;
1814 SkRect fDevBounds;
1815 };
1816
1817 SkMatrix fViewMatrixIfUsingLocalCoords;
1818 Helper fHelper;
1819 SkSTArray<1, Circle, true> fCircles;
1820 int fVertCount;
1821 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001822 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001823
Chris Daltoneb694b72020-03-16 09:25:50 -06001824 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001825 GrProgramInfo* fProgramInfo = nullptr;
1826
John Stiles7571f9e2020-09-02 22:42:33 -04001827 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001828};
1829
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001830///////////////////////////////////////////////////////////////////////////////
1831
Brian Salomon05441c42017-05-15 16:45:49 -04001832class EllipseOp : public GrMeshDrawOp {
1833private:
1834 using Helper = GrSimpleMeshDrawOpHelper;
1835
1836 struct DeviceSpaceParams {
1837 SkPoint fCenter;
1838 SkScalar fXRadius;
1839 SkScalar fYRadius;
1840 SkScalar fInnerXRadius;
1841 SkScalar fInnerYRadius;
1842 };
1843
joshualitt76e7fb62015-02-11 08:52:27 -08001844public:
Brian Salomon25a88092016-12-01 09:36:50 -05001845 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001846
Herb Derbyc76d4092020-10-07 16:46:15 -04001847 static GrOp::Owner Make(GrRecordingContext* context,
1848 GrPaint&& paint,
1849 const SkMatrix& viewMatrix,
1850 const SkRect& ellipse,
1851 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001852 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001853 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001854 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1855 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001856 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1857 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001858 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1859 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1860 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1861 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001862
bsalomon4b4a7cc2016-07-08 04:42:54 -07001863 // do (potentially) anisotropic mapping of stroke
1864 SkVector scaledStroke;
1865 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001866 scaledStroke.fX = SkScalarAbs(
1867 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1868 scaledStroke.fY = SkScalarAbs(
1869 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001870
1871 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001872 bool isStrokeOnly =
1873 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001874 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1875
Brian Salomon05441c42017-05-15 16:45:49 -04001876 params.fInnerXRadius = 0;
1877 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001878 if (hasStroke) {
1879 if (SkScalarNearlyZero(scaledStroke.length())) {
1880 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1881 } else {
1882 scaledStroke.scale(SK_ScalarHalf);
1883 }
1884
1885 // we only handle thick strokes for near-circular ellipses
1886 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001887 (0.5f * params.fXRadius > params.fYRadius ||
1888 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001889 return nullptr;
1890 }
1891
1892 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001893 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1894 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1895 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1896 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001897 return nullptr;
1898 }
1899
1900 // this is legit only if scale & translation (which should be the case at the moment)
1901 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001902 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1903 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001904 }
1905
Brian Salomon05441c42017-05-15 16:45:49 -04001906 params.fXRadius += scaledStroke.fX;
1907 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001908 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001909
1910 // For large ovals with low precision floats, we fall back to the path renderer.
1911 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1912 // minimum value to avoid divides by zero. With large ovals and low precision this
1913 // leads to blurring at the edge of the oval.
1914 const SkScalar kMaxOvalRadius = 16384;
1915 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1916 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1917 return nullptr;
1918 }
1919
Greg Daniel2655ede2019-04-10 00:49:28 +00001920 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001921 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001922 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001923
Herb Derbyc76d4092020-10-07 16:46:15 -04001924 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001925 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001926 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001927 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001928 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001929 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001930 SkStrokeRec::Style style = stroke.getStyle();
1931 bool isStrokeOnly =
1932 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001933
Brian Salomon05441c42017-05-15 16:45:49 -04001934 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1935 params.fInnerXRadius, params.fInnerYRadius,
1936 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1937 params.fCenter.fY - params.fYRadius,
1938 params.fCenter.fX + params.fXRadius,
1939 params.fCenter.fY + params.fYRadius)});
1940
Greg Daniel5faf4742019-10-01 15:14:44 -04001941 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001942
Brian Salomon05441c42017-05-15 16:45:49 -04001943 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1944 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001945 }
joshualitt76e7fb62015-02-11 08:52:27 -08001946
Brian Salomon289e3d82016-12-14 15:52:56 -05001947 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001948
Robert Phillips294723d2021-06-17 09:23:58 -04001949 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001950 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001951 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001952 } else {
1953 fHelper.visitProxies(func);
1954 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001955 }
1956
Chris Dalton57ab06c2021-04-22 12:57:28 -06001957 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1958 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001959 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1960 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001961 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001962 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001963 GrProcessorAnalysisCoverage::kSingleChannel, color,
1964 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001965 }
1966
1967 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1968
bsalomone46f9fe2015-08-18 06:05:14 -07001969private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001970 GrProgramInfo* programInfo() override { return fProgramInfo; }
1971
Robert Phillips4133dc42020-03-11 15:55:55 -04001972 void onCreateProgramInfo(const GrCaps* caps,
1973 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001974 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001975 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001976 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001977 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001978 GrXferBarrierFlags renderPassXferBarriers,
1979 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001980 SkMatrix localMatrix;
1981 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001982 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001983 }
1984
Robert Phillips4490d922020-03-03 14:50:59 -05001985 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1986 fUseScale, localMatrix);
1987
Chris Dalton25da4062021-07-13 14:06:28 -06001988 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
1989 if (usesMSAASurface) {
1990 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
1991 }
1992
1993 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
1994 std::move(appliedClip),
1995 dstProxyView, gp,
1996 fHelper.detachProcessorSet(),
1997 GrPrimitiveType::kTriangles,
1998 renderPassXferBarriers,
1999 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002000 }
2001
Robert Phillips71143952021-06-17 14:55:07 -04002002 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002003 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002004 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002005 if (!fProgramInfo) {
2006 return;
2007 }
2008 }
2009
Robert Phillips787fd9d2021-03-22 14:48:09 -04002010 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002011 GrVertexWriter verts{helper.vertices()};
2012 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05002013 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08002014 return;
2015 }
2016
Chris Dalton25da4062021-07-13 14:06:28 -06002017 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2018 // full sample coverage.
2019 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2020
Brian Salomon05441c42017-05-15 16:45:49 -04002021 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002022 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002023 SkScalar xRadius = ellipse.fXRadius;
2024 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002025
2026 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002027 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2028 SkScalarInvert(xRadius),
2029 SkScalarInvert(yRadius),
2030 SkScalarInvert(ellipse.fInnerXRadius),
2031 SkScalarInvert(ellipse.fInnerYRadius)
2032 };
Chris Dalton25da4062021-07-13 14:06:28 -06002033 SkScalar xMaxOffset = xRadius + aaBloat;
2034 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07002035
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002036 if (!fStroked) {
2037 // For filled ellipses we map a unit circle in the vertex attributes rather than
2038 // computing an ellipse and modifying that distance, so we normalize to 1
2039 xMaxOffset /= xRadius;
2040 yMaxOffset /= yRadius;
2041 }
2042
joshualitt76e7fb62015-02-11 08:52:27 -08002043 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002044 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2045 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002046 color,
2047 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002048 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002049 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002050 }
Robert Phillips4490d922020-03-03 14:50:59 -05002051 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002052 }
2053
2054 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002055 if (!fProgramInfo || !fMesh) {
2056 return;
2057 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002058
Chris Dalton765ed362020-03-16 17:34:44 -06002059 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002060 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002061 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002062 }
2063
Herb Derbye25c3002020-10-27 15:57:27 -04002064 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002065 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002066
Brian Salomon05441c42017-05-15 16:45:49 -04002067 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002068 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002069 }
2070
bsalomoncdaa97b2016-03-08 08:30:14 -08002071 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002072 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002073 }
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002076 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2077 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002078 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002079 }
2080
Brian Salomon05441c42017-05-15 16:45:49 -04002081 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002082 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002083 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002084 }
2085
John Stilesaf366522020-08-13 09:57:34 -04002086#if GR_TEST_UTILS
2087 SkString onDumpInfo() const override {
2088 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2089 for (const auto& geo : fEllipses) {
2090 string.appendf(
2091 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2092 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2093 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2094 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2095 geo.fInnerXRadius, geo.fInnerYRadius);
2096 }
2097 string += fHelper.dumpInfo();
2098 return string;
2099 }
2100#endif
2101
Brian Salomon05441c42017-05-15 16:45:49 -04002102 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002103 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002104 SkScalar fXRadius;
2105 SkScalar fYRadius;
2106 SkScalar fInnerXRadius;
2107 SkScalar fInnerYRadius;
2108 SkRect fDevBounds;
2109 };
joshualitt76e7fb62015-02-11 08:52:27 -08002110
Brian Salomon289e3d82016-12-14 15:52:56 -05002111 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002112 Helper fHelper;
2113 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002114 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002115 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002116 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002117
Chris Daltoneb694b72020-03-16 09:25:50 -06002118 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002119 GrProgramInfo* fProgramInfo = nullptr;
2120
John Stiles7571f9e2020-09-02 22:42:33 -04002121 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002122};
2123
joshualitt76e7fb62015-02-11 08:52:27 -08002124/////////////////////////////////////////////////////////////////////////////////////////////////
2125
Brian Salomon05441c42017-05-15 16:45:49 -04002126class DIEllipseOp : public GrMeshDrawOp {
2127private:
2128 using Helper = GrSimpleMeshDrawOpHelper;
2129
2130 struct DeviceSpaceParams {
2131 SkPoint fCenter;
2132 SkScalar fXRadius;
2133 SkScalar fYRadius;
2134 SkScalar fInnerXRadius;
2135 SkScalar fInnerYRadius;
2136 DIEllipseStyle fStyle;
2137 };
2138
joshualitt76e7fb62015-02-11 08:52:27 -08002139public:
Brian Salomon25a88092016-12-01 09:36:50 -05002140 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002141
Herb Derbyc76d4092020-10-07 16:46:15 -04002142 static GrOp::Owner Make(GrRecordingContext* context,
2143 GrPaint&& paint,
2144 const SkMatrix& viewMatrix,
2145 const SkRect& ellipse,
2146 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002147 DeviceSpaceParams params;
2148 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2149 params.fXRadius = SkScalarHalf(ellipse.width());
2150 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002151
bsalomon4b4a7cc2016-07-08 04:42:54 -07002152 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002153 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2154 ? DIEllipseStyle::kStroke
2155 : (SkStrokeRec::kHairline_Style == style)
2156 ? DIEllipseStyle::kHairline
2157 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002158
Brian Salomon05441c42017-05-15 16:45:49 -04002159 params.fInnerXRadius = 0;
2160 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002161 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2162 SkScalar strokeWidth = stroke.getWidth();
2163
2164 if (SkScalarNearlyZero(strokeWidth)) {
2165 strokeWidth = SK_ScalarHalf;
2166 } else {
2167 strokeWidth *= SK_ScalarHalf;
2168 }
2169
2170 // we only handle thick strokes for near-circular ellipses
2171 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002172 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2173 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002174 return nullptr;
2175 }
2176
2177 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002178 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2179 (strokeWidth * strokeWidth) * params.fXRadius) {
2180 return nullptr;
2181 }
2182 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2183 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002184 return nullptr;
2185 }
2186
2187 // set inner radius (if needed)
2188 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002189 params.fInnerXRadius = params.fXRadius - strokeWidth;
2190 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002191 }
2192
Brian Salomon05441c42017-05-15 16:45:49 -04002193 params.fXRadius += strokeWidth;
2194 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002195 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002196
2197 // For large ovals with low precision floats, we fall back to the path renderer.
2198 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2199 // minimum value to avoid divides by zero. With large ovals and low precision this
2200 // leads to blurring at the edge of the oval.
2201 const SkScalar kMaxOvalRadius = 16384;
2202 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2203 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2204 return nullptr;
2205 }
2206
Brian Salomon05441c42017-05-15 16:45:49 -04002207 if (DIEllipseStyle::kStroke == params.fStyle &&
2208 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2209 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002210 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002211 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002212 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002213
Herb Derbyc76d4092020-10-07 16:46:15 -04002214 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002215 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002216 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002217 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002218 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002219 // This expands the outer rect so that after CTM we end up with a half-pixel border
2220 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2221 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2222 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2223 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002224 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2225 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002226
Brian Salomon05441c42017-05-15 16:45:49 -04002227 fEllipses.emplace_back(
2228 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2229 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002230 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2231 params.fCenter.fY - params.fYRadius,
2232 params.fCenter.fX + params.fXRadius,
2233 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002234 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002235 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002236 }
2237
Brian Salomon289e3d82016-12-14 15:52:56 -05002238 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002239
Robert Phillips294723d2021-06-17 09:23:58 -04002240 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002241 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002242 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002243 } else {
2244 fHelper.visitProxies(func);
2245 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002246 }
2247
Chris Dalton57ab06c2021-04-22 12:57:28 -06002248 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2249 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002250 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2251 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002252 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002253 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002254 GrProcessorAnalysisCoverage::kSingleChannel, color,
2255 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002256 }
2257
2258 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2259
bsalomone46f9fe2015-08-18 06:05:14 -07002260private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002261 GrProgramInfo* programInfo() override { return fProgramInfo; }
2262
Robert Phillips4133dc42020-03-11 15:55:55 -04002263 void onCreateProgramInfo(const GrCaps* caps,
2264 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002265 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002266 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002267 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002268 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002269 GrXferBarrierFlags renderPassXferBarriers,
2270 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002271 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2272 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002273 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002274
Chris Dalton25da4062021-07-13 14:06:28 -06002275 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2276 if (usesMSAASurface) {
2277 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2278 }
2279
2280 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2281 std::move(appliedClip),
2282 dstProxyView, gp,
2283 fHelper.detachProcessorSet(),
2284 GrPrimitiveType::kTriangles,
2285 renderPassXferBarriers,
2286 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002287 }
2288
Robert Phillips71143952021-06-17 14:55:07 -04002289 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002290 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002291 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002292 }
2293
Robert Phillips787fd9d2021-03-22 14:48:09 -04002294 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002295 GrVertexWriter verts{helper.vertices()};
2296 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002297 return;
2298 }
2299
Brian Salomon05441c42017-05-15 16:45:49 -04002300 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002301 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002302 SkScalar xRadius = ellipse.fXRadius;
2303 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002304
Chris Dalton25da4062021-07-13 14:06:28 -06002305 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2306 // full sample coverage.
2307 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2308 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2309 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002310
Chris Dalton25da4062021-07-13 14:06:28 -06002311 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2312 // occurs at x^2 + y^2 == 1.
2313 float outerCoordX = drawBounds.width() / (xRadius * 2);
2314 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002315
Chris Dalton25da4062021-07-13 14:06:28 -06002316 // By default, constructed so that inner coord is (0, 0) for all points
2317 float innerCoordX = 0;
2318 float innerCoordY = 0;
2319
2320 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2321 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002322 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002323 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2324 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002325 }
joshualitt76e7fb62015-02-11 08:52:27 -08002326
Chris Dalton25da4062021-07-13 14:06:28 -06002327 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002328 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002329 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002330 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002331 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002332 }
Robert Phillips4490d922020-03-03 14:50:59 -05002333 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002334 }
2335
2336 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002337 if (!fProgramInfo || !fMesh) {
2338 return;
2339 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002340
Chris Dalton765ed362020-03-16 17:34:44 -06002341 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002342 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002343 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002344 }
halcanary9d524f22016-03-29 09:03:52 -07002345
Herb Derbye25c3002020-10-27 15:57:27 -04002346 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002347 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002348 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002349 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002350 }
2351
bsalomoncdaa97b2016-03-08 08:30:14 -08002352 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002353 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002354 }
2355
joshualittd96a67b2015-05-05 14:09:05 -07002356 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002357 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002358 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002359 }
2360
Brian Salomon05441c42017-05-15 16:45:49 -04002361 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002362 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002363 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002364 }
2365
John Stilesaf366522020-08-13 09:57:34 -04002366#if GR_TEST_UTILS
2367 SkString onDumpInfo() const override {
2368 SkString string;
2369 for (const auto& geo : fEllipses) {
2370 string.appendf(
2371 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2372 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2373 "GeoDY: %.2f\n",
2374 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2375 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2376 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2377 }
2378 string += fHelper.dumpInfo();
2379 return string;
2380 }
2381#endif
2382
Brian Salomon05441c42017-05-15 16:45:49 -04002383 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2384 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002385
Brian Salomon05441c42017-05-15 16:45:49 -04002386 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002387 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002388 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002389 SkScalar fXRadius;
2390 SkScalar fYRadius;
2391 SkScalar fInnerXRadius;
2392 SkScalar fInnerYRadius;
2393 SkScalar fGeoDx;
2394 SkScalar fGeoDy;
2395 DIEllipseStyle fStyle;
2396 SkRect fBounds;
2397 };
2398
Brian Salomon05441c42017-05-15 16:45:49 -04002399 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002400 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002401 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002402 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002403
Chris Daltoneb694b72020-03-16 09:25:50 -06002404 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002405 GrProgramInfo* fProgramInfo = nullptr;
2406
John Stiles7571f9e2020-09-02 22:42:33 -04002407 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002408};
2409
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002410///////////////////////////////////////////////////////////////////////////////
2411
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002412// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002413//
2414// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2415// ____________
2416// |_|________|_|
2417// | | | |
2418// | | | |
2419// | | | |
2420// |_|________|_|
2421// |_|________|_|
2422//
2423// For strokes, we don't draw the center quad.
2424//
2425// For circular roundrects, in the case where the stroke width is greater than twice
2426// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002427// in the center. The shared vertices are duplicated so we can set a different outer radius
2428// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002429// ____________
2430// |_|________|_|
2431// | |\ ____ /| |
2432// | | | | | |
2433// | | |____| | |
2434// |_|/______\|_|
2435// |_|________|_|
2436//
2437// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002438//
2439// For filled rrects that need to provide a distance vector we resuse the overstroke
2440// geometry but make the inner rect degenerate (either a point or a horizontal or
2441// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002442
jvanverth84839f62016-08-29 10:16:40 -07002443static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002444 // clang-format off
2445 // overstroke quads
2446 // we place this at the beginning so that we can skip these indices when rendering normally
2447 16, 17, 19, 16, 19, 18,
2448 19, 17, 23, 19, 23, 21,
2449 21, 23, 22, 21, 22, 20,
2450 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002451
Brian Salomon289e3d82016-12-14 15:52:56 -05002452 // corners
2453 0, 1, 5, 0, 5, 4,
2454 2, 3, 7, 2, 7, 6,
2455 8, 9, 13, 8, 13, 12,
2456 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002457
Brian Salomon289e3d82016-12-14 15:52:56 -05002458 // edges
2459 1, 2, 6, 1, 6, 5,
2460 4, 5, 9, 4, 9, 8,
2461 6, 7, 11, 6, 11, 10,
2462 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002463
Brian Salomon289e3d82016-12-14 15:52:56 -05002464 // center
2465 // we place this at the end so that we can ignore these indices when not rendering as filled
2466 5, 6, 10, 5, 10, 9,
2467 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002468};
Brian Salomon289e3d82016-12-14 15:52:56 -05002469
jvanverth84839f62016-08-29 10:16:40 -07002470// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002471static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002472
jvanverth84839f62016-08-29 10:16:40 -07002473// overstroke count is arraysize minus the center indices
2474static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2475// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002476static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002477// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002478static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2479static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002480static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002481
jvanverthc3d0e422016-08-25 08:12:35 -07002482enum RRectType {
2483 kFill_RRectType,
2484 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002485 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002486};
2487
jvanverth84839f62016-08-29 10:16:40 -07002488static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002489 switch (type) {
2490 case kFill_RRectType:
2491 case kStroke_RRectType:
2492 return kVertsPerStandardRRect;
2493 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002494 return kVertsPerOverstrokeRRect;
2495 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002496 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002497}
2498
2499static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002500 switch (type) {
2501 case kFill_RRectType:
2502 return kIndicesPerFillRRect;
2503 case kStroke_RRectType:
2504 return kIndicesPerStrokeRRect;
2505 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002506 return kIndicesPerOverstrokeRRect;
2507 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002508 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002509}
2510
2511static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002512 switch (type) {
2513 case kFill_RRectType:
2514 case kStroke_RRectType:
2515 return gStandardRRectIndices;
2516 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002517 return gOverstrokeRRectIndices;
2518 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002519 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002520}
2521
joshualitt76e7fb62015-02-11 08:52:27 -08002522///////////////////////////////////////////////////////////////////////////////////////////////////
2523
Robert Phillips79839d42016-10-06 15:03:34 -04002524// For distance computations in the interior of filled rrects we:
2525//
2526// add a interior degenerate (point or line) rect
2527// each vertex of that rect gets -outerRad as its radius
2528// this makes the computation of the distance to the outer edge be negative
2529// negative values are caught and then handled differently in the GP's onEmitCode
2530// each vertex is also given the normalized x & y distance from the interior rect's edge
2531// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2532
Brian Salomon05441c42017-05-15 16:45:49 -04002533class CircularRRectOp : public GrMeshDrawOp {
2534private:
2535 using Helper = GrSimpleMeshDrawOpHelper;
2536
joshualitt76e7fb62015-02-11 08:52:27 -08002537public:
Brian Salomon25a88092016-12-01 09:36:50 -05002538 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002539
bsalomon4b4a7cc2016-07-08 04:42:54 -07002540 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2541 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002542 static GrOp::Owner Make(GrRecordingContext* context,
2543 GrPaint&& paint,
2544 const SkMatrix& viewMatrix,
2545 const SkRect& devRect,
2546 float devRadius,
2547 float devStrokeWidth,
2548 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002549 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002550 devRect, devRadius,
2551 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002552 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002553 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002554 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2555 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002556 : INHERITED(ClassID())
2557 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002558 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002559 SkRect bounds = devRect;
2560 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2561 SkScalar innerRadius = 0.0f;
2562 SkScalar outerRadius = devRadius;
2563 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002564 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002565 if (devStrokeWidth > 0) {
2566 if (SkScalarNearlyZero(devStrokeWidth)) {
2567 halfWidth = SK_ScalarHalf;
2568 } else {
2569 halfWidth = SkScalarHalf(devStrokeWidth);
2570 }
joshualitt76e7fb62015-02-11 08:52:27 -08002571
bsalomon4b4a7cc2016-07-08 04:42:54 -07002572 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002573 // Outset stroke by 1/4 pixel
2574 devStrokeWidth += 0.25f;
2575 // If stroke is greater than width or height, this is still a fill
2576 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002577 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002578 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002579 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002580 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002581 }
2582 outerRadius += halfWidth;
2583 bounds.outset(halfWidth, halfWidth);
2584 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002585
Greg Daniel2655ede2019-04-10 00:49:28 +00002586 // The radii are outset for two reasons. First, it allows the shader to simply perform
2587 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2588 // Second, the outer radius is used to compute the verts of the bounding box that is
2589 // rendered and the outset ensures the box will cover all partially covered by the rrect
2590 // corners.
2591 outerRadius += SK_ScalarHalf;
2592 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002593
Greg Daniel5faf4742019-10-01 15:14:44 -04002594 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002595
Greg Daniel2655ede2019-04-10 00:49:28 +00002596 // Expand the rect for aa to generate correct vertices.
2597 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002598
Brian Salomon05441c42017-05-15 16:45:49 -04002599 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002600 fVertCount = rrect_type_to_vert_count(type);
2601 fIndexCount = rrect_type_to_index_count(type);
2602 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002603 }
2604
Brian Salomon289e3d82016-12-14 15:52:56 -05002605 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002606
Robert Phillips294723d2021-06-17 09:23:58 -04002607 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002608 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002609 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002610 } else {
2611 fHelper.visitProxies(func);
2612 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002613 }
2614
Chris Dalton57ab06c2021-04-22 12:57:28 -06002615 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2616 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002617 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002618 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002619 GrProcessorAnalysisCoverage::kSingleChannel, color,
2620 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002621 }
2622
2623 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2624
Brian Salomon92aee3d2016-12-21 09:20:25 -05002625private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002626 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002627 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002628 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002629 SkASSERT(smInset < bigInset);
2630
2631 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002632 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2633 color,
2634 xOffset, 0.0f,
2635 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002636
2637 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002638 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2639 color,
2640 xOffset, 0.0f,
2641 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002642
Brian Osmana1d4eb92018-12-06 16:33:10 -05002643 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2644 color,
2645 0.0f, 0.0f,
2646 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002647
Brian Osmana1d4eb92018-12-06 16:33:10 -05002648 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2649 color,
2650 0.0f, 0.0f,
2651 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002652
Brian Osmana1d4eb92018-12-06 16:33:10 -05002653 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2654 color,
2655 0.0f, 0.0f,
2656 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002657
Brian Osmana1d4eb92018-12-06 16:33:10 -05002658 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2659 color,
2660 0.0f, 0.0f,
2661 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002662
2663 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002664 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2665 color,
2666 xOffset, 0.0f,
2667 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002668
2669 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002670 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2671 color,
2672 xOffset, 0.0f,
2673 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002674 }
2675
Robert Phillips2669a7b2020-03-12 12:07:19 -04002676 GrProgramInfo* programInfo() override { return fProgramInfo; }
2677
Robert Phillips4133dc42020-03-11 15:55:55 -04002678 void onCreateProgramInfo(const GrCaps* caps,
2679 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002680 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002681 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002682 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002683 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002684 GrXferBarrierFlags renderPassXferBarriers,
2685 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002686 SkASSERT(!usesMSAASurface);
2687
bsalomoncdaa97b2016-03-08 08:30:14 -08002688 // Invert the view matrix as a local matrix (if any other processors require coords).
2689 SkMatrix localMatrix;
2690 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002691 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002692 }
2693
Robert Phillips4490d922020-03-03 14:50:59 -05002694 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002695 false, false, false, false,
2696 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002697
Brian Salomon8afde5f2020-04-01 16:22:00 -04002698 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002699 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002700 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002701 }
2702
Robert Phillips71143952021-06-17 14:55:07 -04002703 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002704 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002705 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002706 if (!fProgramInfo) {
2707 return;
2708 }
2709 }
2710
Brian Salomon12d22642019-01-29 14:38:50 -05002711 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002712 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002713
Robert Phillips787fd9d2021-03-22 14:48:09 -04002714 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002715 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002716 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002717 SkDebugf("Could not allocate vertices\n");
2718 return;
2719 }
2720
Brian Salomon12d22642019-01-29 14:38:50 -05002721 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002722 int firstIndex = 0;
2723 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2724 if (!indices) {
2725 SkDebugf("Could not allocate indices\n");
2726 return;
2727 }
2728
2729 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002730 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002731 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002732 SkScalar outerRadius = rrect.fOuterRadius;
2733 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002734
Brian Salomon289e3d82016-12-14 15:52:56 -05002735 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2736 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002737
Brian Salomon289e3d82016-12-14 15:52:56 -05002738 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002739 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002740 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002741 SkScalar innerRadius = rrect.fType != kFill_RRectType
2742 ? rrect.fInnerRadius / rrect.fOuterRadius
2743 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002744 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002745 verts.write(bounds.fLeft, yCoords[i],
2746 color,
2747 -1.0f, yOuterRadii[i],
2748 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002749
Brian Osmana1d4eb92018-12-06 16:33:10 -05002750 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2751 color,
2752 0.0f, yOuterRadii[i],
2753 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002754
Brian Osmana1d4eb92018-12-06 16:33:10 -05002755 verts.write(bounds.fRight - outerRadius, yCoords[i],
2756 color,
2757 0.0f, yOuterRadii[i],
2758 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002759
Brian Osmana1d4eb92018-12-06 16:33:10 -05002760 verts.write(bounds.fRight, yCoords[i],
2761 color,
2762 1.0f, yOuterRadii[i],
2763 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002764 }
jvanverthc3d0e422016-08-25 08:12:35 -07002765 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002766 // Effectively this is an additional stroked rrect, with its
2767 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2768 // This will give us correct AA in the center and the correct
2769 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002770 //
jvanvertha4f1af82016-08-29 07:17:47 -07002771 // Also, the outer offset is a constant vector pointing to the right, which
2772 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002773 if (kOverstroke_RRectType == rrect.fType) {
2774 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002775
Brian Salomon05441c42017-05-15 16:45:49 -04002776 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002777 // this is the normalized distance from the outer rectangle of this
2778 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002779 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002780
Brian Osmana1d4eb92018-12-06 16:33:10 -05002781 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002782 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002783 }
jvanverth6a397612016-08-26 08:15:33 -07002784
Brian Salomon05441c42017-05-15 16:45:49 -04002785 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2786 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002787 for (int i = 0; i < primIndexCount; ++i) {
2788 *indices++ = primIndices[i] + currStartVertex;
2789 }
2790
Brian Salomon05441c42017-05-15 16:45:49 -04002791 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002792 }
2793
Robert Phillips4490d922020-03-03 14:50:59 -05002794 fMesh = target->allocMesh();
2795 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002796 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002797 }
2798
2799 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002800 if (!fProgramInfo || !fMesh) {
2801 return;
2802 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002803
Chris Dalton765ed362020-03-16 17:34:44 -06002804 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002805 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002806 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002807 }
2808
Herb Derbye25c3002020-10-27 15:57:27 -04002809 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002810 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002811
2812 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002813 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002814 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002815 }
2816
Brian Salomon05441c42017-05-15 16:45:49 -04002817 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002818 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002819 }
2820
Brian Salomon05441c42017-05-15 16:45:49 -04002821 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002822 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2823 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002824 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002825 }
2826
Brian Salomon05441c42017-05-15 16:45:49 -04002827 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002828 fVertCount += that->fVertCount;
2829 fIndexCount += that->fIndexCount;
2830 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002831 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002832 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002833 }
2834
John Stilesaf366522020-08-13 09:57:34 -04002835#if GR_TEST_UTILS
2836 SkString onDumpInfo() const override {
2837 SkString string;
2838 for (int i = 0; i < fRRects.count(); ++i) {
2839 string.appendf(
2840 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2841 "InnerRad: %.2f, OuterRad: %.2f\n",
2842 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2843 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2844 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2845 fRRects[i].fOuterRadius);
2846 }
2847 string += fHelper.dumpInfo();
2848 return string;
2849 }
2850#endif
2851
Brian Salomon05441c42017-05-15 16:45:49 -04002852 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002853 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002854 SkScalar fInnerRadius;
2855 SkScalar fOuterRadius;
2856 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002857 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002858 };
2859
Brian Salomon289e3d82016-12-14 15:52:56 -05002860 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002861 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002862 int fVertCount;
2863 int fIndexCount;
2864 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002865 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002866 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002867
Chris Daltoneb694b72020-03-16 09:25:50 -06002868 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002869 GrProgramInfo* fProgramInfo = nullptr;
2870
John Stiles7571f9e2020-09-02 22:42:33 -04002871 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002872};
2873
jvanverth84839f62016-08-29 10:16:40 -07002874static const int kNumRRectsInIndexBuffer = 256;
2875
2876GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2877GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002878static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2879 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002880 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2881 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2882 switch (type) {
2883 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002884 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002885 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2886 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002887 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002888 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002889 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2890 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002891 default:
2892 SkASSERT(false);
2893 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002894 }
jvanverth84839f62016-08-29 10:16:40 -07002895}
2896
Brian Salomon05441c42017-05-15 16:45:49 -04002897class EllipticalRRectOp : public GrMeshDrawOp {
2898private:
2899 using Helper = GrSimpleMeshDrawOpHelper;
2900
joshualitt76e7fb62015-02-11 08:52:27 -08002901public:
Brian Salomon25a88092016-12-01 09:36:50 -05002902 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002903
bsalomon4b4a7cc2016-07-08 04:42:54 -07002904 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2905 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002906 static GrOp::Owner Make(GrRecordingContext* context,
2907 GrPaint&& paint,
2908 const SkMatrix& viewMatrix,
2909 const SkRect& devRect,
2910 float devXRadius,
2911 float devYRadius,
2912 SkVector devStrokeWidths,
2913 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002914 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2915 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002916 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2917 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002918 if (devStrokeWidths.fX > 0) {
2919 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2920 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2921 } else {
2922 devStrokeWidths.scale(SK_ScalarHalf);
2923 }
joshualitt76e7fb62015-02-11 08:52:27 -08002924
bsalomon4b4a7cc2016-07-08 04:42:54 -07002925 // we only handle thick strokes for near-circular ellipses
2926 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002927 (SK_ScalarHalf * devXRadius > devYRadius ||
2928 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002929 return nullptr;
2930 }
2931
2932 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002933 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2934 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002935 return nullptr;
2936 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002937 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2938 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002939 return nullptr;
2940 }
Brian Salomon05441c42017-05-15 16:45:49 -04002941 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002942 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002943 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002944 devXRadius, devYRadius, devStrokeWidths,
2945 strokeOnly);
2946 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002947
Herb Derbyc76d4092020-10-07 16:46:15 -04002948 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002949 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2950 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002951 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002952 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002953 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002954 SkScalar innerXRadius = 0.0f;
2955 SkScalar innerYRadius = 0.0f;
2956 SkRect bounds = devRect;
2957 bool stroked = false;
2958 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002959 // this is legit only if scale & translation (which should be the case at the moment)
2960 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002961 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2962 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002963 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2964 }
2965
Brian Salomon05441c42017-05-15 16:45:49 -04002966 devXRadius += devStrokeHalfWidths.fX;
2967 devYRadius += devStrokeHalfWidths.fY;
2968 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002969 }
2970
Brian Salomon05441c42017-05-15 16:45:49 -04002971 fStroked = stroked;
2972 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002973 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002974 fRRects.emplace_back(
2975 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002976 }
2977
Brian Salomon289e3d82016-12-14 15:52:56 -05002978 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002979
Robert Phillips294723d2021-06-17 09:23:58 -04002980 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002981 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002982 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002983 } else {
2984 fHelper.visitProxies(func);
2985 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002986 }
2987
Chris Dalton57ab06c2021-04-22 12:57:28 -06002988 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2989 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002990 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002991 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002992 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002993 GrProcessorAnalysisCoverage::kSingleChannel, color,
2994 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002995 }
2996
2997 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2998
bsalomone46f9fe2015-08-18 06:05:14 -07002999private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04003000 GrProgramInfo* programInfo() override { return fProgramInfo; }
3001
Robert Phillips4133dc42020-03-11 15:55:55 -04003002 void onCreateProgramInfo(const GrCaps* caps,
3003 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05003004 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06003005 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04003006 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04003007 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05003008 GrXferBarrierFlags renderPassXferBarriers,
3009 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08003010 SkMatrix localMatrix;
3011 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04003012 return;
joshualitt76e7fb62015-02-11 08:52:27 -08003013 }
3014
Robert Phillips4490d922020-03-03 14:50:59 -05003015 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
3016 fUseScale, localMatrix);
3017
Chris Dalton25da4062021-07-13 14:06:28 -06003018 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
3019 if (usesMSAASurface) {
3020 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
3021 }
3022
3023 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
3024 std::move(appliedClip),
3025 dstProxyView, gp,
3026 fHelper.detachProcessorSet(),
3027 GrPrimitiveType::kTriangles,
3028 renderPassXferBarriers,
3029 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05003030 }
3031
Robert Phillips71143952021-06-17 14:55:07 -04003032 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003033 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04003034 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05003035 if (!fProgramInfo) {
3036 return;
3037 }
3038 }
joshualitt76e7fb62015-02-11 08:52:27 -08003039
bsalomonb5238a72015-05-05 07:49:49 -07003040 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07003041 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04003042 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3043 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003044
Brian Salomon12d22642019-01-29 14:38:50 -05003045 if (!indexBuffer) {
3046 SkDebugf("Could not allocate indices\n");
3047 return;
3048 }
Robert Phillips4490d922020-03-03 14:50:59 -05003049 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003050 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003051 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003052 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003053 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003054 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003055 SkDebugf("Could not allocate vertices\n");
3056 return;
3057 }
3058
Brian Salomon05441c42017-05-15 16:45:49 -04003059 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003060 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003061 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003062 float reciprocalRadii[4] = {
3063 SkScalarInvert(rrect.fXRadius),
3064 SkScalarInvert(rrect.fYRadius),
3065 SkScalarInvert(rrect.fInnerXRadius),
3066 SkScalarInvert(rrect.fInnerYRadius)
3067 };
joshualitt76e7fb62015-02-11 08:52:27 -08003068
Brian Osmane3afdd52020-10-28 10:49:56 -04003069 // If the stroke width is exactly double the radius, the inner radii will be zero.
3070 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3071 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3072 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3073
Chris Dalton25da4062021-07-13 14:06:28 -06003074 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3075 // full sample coverage.
3076 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3077
3078 // Extend out the radii to antialias.
3079 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3080 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003081
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003082 SkScalar xMaxOffset = xOuterRadius;
3083 SkScalar yMaxOffset = yOuterRadius;
3084 if (!fStroked) {
3085 // For filled rrects we map a unit circle in the vertex attributes rather than
3086 // computing an ellipse and modifying that distance, so we normalize to 1.
3087 xMaxOffset /= rrect.fXRadius;
3088 yMaxOffset /= rrect.fYRadius;
3089 }
3090
Chris Dalton25da4062021-07-13 14:06:28 -06003091 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003092
Brian Salomon289e3d82016-12-14 15:52:56 -05003093 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3094 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003095 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003096 SK_ScalarNearlyZero, // we're using inversesqrt() in
3097 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003098 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003099
Brian Osman788b9162020-02-07 10:36:46 -05003100 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003101 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003102 verts.write(bounds.fLeft, yCoords[i],
3103 color,
3104 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003105 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003106 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003107
Brian Osmana1d4eb92018-12-06 16:33:10 -05003108 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3109 color,
3110 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003111 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003112 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003113
Brian Osmana1d4eb92018-12-06 16:33:10 -05003114 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3115 color,
3116 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003117 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003118 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003119
Brian Osmana1d4eb92018-12-06 16:33:10 -05003120 verts.write(bounds.fRight, yCoords[i],
3121 color,
3122 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003123 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003124 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003125 }
3126 }
Robert Phillips4490d922020-03-03 14:50:59 -05003127 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003128 }
3129
3130 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003131 if (!fProgramInfo || !fMesh) {
3132 return;
3133 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003134
Chris Dalton765ed362020-03-16 17:34:44 -06003135 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003136 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003137 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003138 }
3139
Herb Derbye25c3002020-10-27 15:57:27 -04003140 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003141 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003142
Brian Salomon05441c42017-05-15 16:45:49 -04003143 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003144 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003145 }
3146
bsalomoncdaa97b2016-03-08 08:30:14 -08003147 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003148 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003149 }
3150
Brian Salomon05441c42017-05-15 16:45:49 -04003151 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003152 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3153 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003154 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003155 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003156
Brian Salomon05441c42017-05-15 16:45:49 -04003157 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003158 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003159 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003160 }
3161
John Stilesaf366522020-08-13 09:57:34 -04003162#if GR_TEST_UTILS
3163 SkString onDumpInfo() const override {
3164 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3165 for (const auto& geo : fRRects) {
3166 string.appendf(
3167 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3168 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3169 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3170 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3171 geo.fInnerXRadius, geo.fInnerYRadius);
3172 }
3173 string += fHelper.dumpInfo();
3174 return string;
3175 }
3176#endif
3177
Brian Salomon05441c42017-05-15 16:45:49 -04003178 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003179 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003180 SkScalar fXRadius;
3181 SkScalar fYRadius;
3182 SkScalar fInnerXRadius;
3183 SkScalar fInnerYRadius;
3184 SkRect fDevBounds;
3185 };
3186
Brian Salomon289e3d82016-12-14 15:52:56 -05003187 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003188 Helper fHelper;
3189 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003190 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003191 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003192 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003193
Chris Daltoneb694b72020-03-16 09:25:50 -06003194 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003195 GrProgramInfo* fProgramInfo = nullptr;
3196
John Stiles7571f9e2020-09-02 22:42:33 -04003197 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003198};
3199
Herb Derbyc76d4092020-10-07 16:46:15 -04003200GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3201 GrPaint&& paint,
3202 const SkMatrix& viewMatrix,
3203 const SkRRect& rrect,
3204 const SkStrokeRec& stroke,
3205 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003206 SkASSERT(viewMatrix.rectStaysRect());
3207 SkASSERT(viewMatrix.isSimilarity());
3208 SkASSERT(rrect.isSimple());
3209 SkASSERT(!rrect.isOval());
3210 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3211
3212 // RRect ops only handle simple, but not too simple, rrects.
3213 // Do any matrix crunching before we reset the draw state for device coords.
3214 const SkRect& rrectBounds = rrect.getBounds();
3215 SkRect bounds;
3216 viewMatrix.mapRect(&bounds, rrectBounds);
3217
3218 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3219 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3220 viewMatrix[SkMatrix::kMSkewY]));
3221
3222 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3223 SkScalar scaledStroke = -1;
3224 SkScalar strokeWidth = stroke.getWidth();
3225 SkStrokeRec::Style style = stroke.getStyle();
3226
3227 bool isStrokeOnly =
3228 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3229 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3230
3231 if (hasStroke) {
3232 if (SkStrokeRec::kHairline_Style == style) {
3233 scaledStroke = SK_Scalar1;
3234 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003235 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3236 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003237 }
3238 }
3239
3240 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3241 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3242 // patch will have fractional coverage. This only matters when the interior is actually filled.
3243 // We could consider falling back to rect rendering here, since a tiny radius is
3244 // indistinguishable from a square corner.
3245 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3246 return nullptr;
3247 }
3248
3249 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3250 scaledStroke, isStrokeOnly);
3251}
3252
Herb Derbyc76d4092020-10-07 16:46:15 -04003253GrOp::Owner make_rrect_op(GrRecordingContext* context,
3254 GrPaint&& paint,
3255 const SkMatrix& viewMatrix,
3256 const SkRRect& rrect,
3257 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003258 SkASSERT(viewMatrix.rectStaysRect());
3259 SkASSERT(rrect.isSimple());
3260 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003261
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003262 // RRect ops only handle simple, but not too simple, rrects.
3263 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003264 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003265 SkRect bounds;
3266 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003267
Mike Reed242135a2018-02-22 13:41:39 -05003268 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003269 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3270 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3271 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3272 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003273
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003274 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003275
bsalomon4b4a7cc2016-07-08 04:42:54 -07003276 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3277 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003278 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003279
Brian Salomon289e3d82016-12-14 15:52:56 -05003280 bool isStrokeOnly =
3281 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003282 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3283
3284 if (hasStroke) {
3285 if (SkStrokeRec::kHairline_Style == style) {
3286 scaledStroke.set(1, 1);
3287 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003288 scaledStroke.fX = SkScalarAbs(
3289 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3290 scaledStroke.fY = SkScalarAbs(
3291 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003292 }
3293
Jim Van Verth64b85892019-06-17 12:01:46 -04003294 // if half of strokewidth is greater than radius, we don't handle that right now
3295 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3296 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003297 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003298 }
3299 }
3300
Brian Salomon8a97f562019-04-18 14:07:27 -04003301 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003302 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003303 std::swap(xRadius, yRadius);
3304 std::swap(scaledStroke.fX, scaledStroke.fY);
3305 }
3306
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003307 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3308 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3309 // patch will have fractional coverage. This only matters when the interior is actually filled.
3310 // We could consider falling back to rect rendering here, since a tiny radius is
3311 // indistinguishable from a square corner.
3312 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003313 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003314 }
3315
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003316 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003317 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3318 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003319}
3320
Herb Derbyc76d4092020-10-07 16:46:15 -04003321GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3322 GrPaint&& paint,
3323 const SkMatrix& viewMatrix,
3324 const SkRRect& rrect,
3325 const SkStrokeRec& stroke,
3326 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003327 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003328 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003329 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003330 }
3331
3332 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003333 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003334 }
3335
Greg Daniel2655ede2019-04-10 00:49:28 +00003336 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003337}
joshualitt3e708c52015-04-30 13:49:27 -07003338
bsalomon4b4a7cc2016-07-08 04:42:54 -07003339///////////////////////////////////////////////////////////////////////////////
3340
Herb Derbyc76d4092020-10-07 16:46:15 -04003341GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3342 GrPaint&& paint,
3343 const SkMatrix& viewMatrix,
3344 const SkRect& oval,
3345 const GrStyle& style,
3346 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003347 SkScalar width = oval.width();
3348 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3349 circle_stays_circle(viewMatrix));
3350
3351 auto r = width / 2.f;
3352 SkPoint center = { oval.centerX(), oval.centerY() };
3353 if (style.hasNonDashPathEffect()) {
3354 return nullptr;
3355 } else if (style.isDashed()) {
3356 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3357 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3358 return nullptr;
3359 }
3360 auto onInterval = style.dashIntervals()[0];
3361 auto offInterval = style.dashIntervals()[1];
3362 if (offInterval == 0) {
3363 GrStyle strokeStyle(style.strokeRec(), nullptr);
3364 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3365 strokeStyle, shaderCaps);
3366 } else if (onInterval == 0) {
3367 // There is nothing to draw but we have no way to indicate that here.
3368 return nullptr;
3369 }
3370 auto angularOnInterval = onInterval / r;
3371 auto angularOffInterval = offInterval / r;
3372 auto phaseAngle = style.dashPhase() / r;
3373 // Currently this function doesn't accept ovals with different start angles, though
3374 // it could.
3375 static const SkScalar kStartAngle = 0.f;
3376 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3377 style.strokeRec().getWidth(), kStartAngle,
3378 angularOnInterval, angularOffInterval, phaseAngle);
3379 }
3380 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3381}
3382
Herb Derbyc76d4092020-10-07 16:46:15 -04003383GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3384 GrPaint&& paint,
3385 const SkMatrix& viewMatrix,
3386 const SkRect& oval,
3387 const GrStyle& style,
3388 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003389 if (style.pathEffect()) {
3390 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003391 }
3392
Stan Ilieveb868aa2017-02-21 11:06:16 -05003393 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003394 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003395 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003396 }
3397
Stan Ilieveb868aa2017-02-21 11:06:16 -05003398 // Otherwise, if we have shader derivative support, render as device-independent
3399 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003400 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3401 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3402 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3403 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3404 // Check for near-degenerate matrix
3405 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003406 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003407 style.strokeRec());
3408 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003409 }
3410
bsalomon4b4a7cc2016-07-08 04:42:54 -07003411 return nullptr;
3412}
3413
3414///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003415
Herb Derbyc76d4092020-10-07 16:46:15 -04003416GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3417 GrPaint&& paint,
3418 const SkMatrix& viewMatrix,
3419 const SkRect& oval, SkScalar startAngle,
3420 SkScalar sweepAngle, bool useCenter,
3421 const GrStyle& style,
3422 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003423 SkASSERT(!oval.isEmpty());
3424 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003425 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003426 if (SkScalarAbs(sweepAngle) >= 360.f) {
3427 return nullptr;
3428 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003429 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3430 return nullptr;
3431 }
3432 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003433 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3434 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003435 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003436 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003437}
3438
3439///////////////////////////////////////////////////////////////////////////////
3440
Hal Canary6f6961e2017-01-31 13:50:44 -05003441#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003442
Brian Salomon05441c42017-05-15 16:45:49 -04003443GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003444 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003445 SkScalar rotate = random->nextSScalar1() * 360.f;
3446 SkScalar translateX = random->nextSScalar1() * 1000.f;
3447 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003448 SkScalar scale;
3449 do {
3450 scale = random->nextSScalar1() * 100.f;
3451 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003452 SkMatrix viewMatrix;
3453 viewMatrix.setRotate(rotate);
3454 viewMatrix.postTranslate(translateX, translateY);
3455 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003456 SkRect circle = GrTest::TestSquare(random);
3457 SkPoint center = {circle.centerX(), circle.centerY()};
3458 SkScalar radius = circle.width() / 2.f;
3459 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003460 CircleOp::ArcParams arcParamsTmp;
3461 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003462 if (random->nextBool()) {
3463 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003464 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3465 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003466 arcParams = &arcParamsTmp;
3467 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003468 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3469 center, radius,
3470 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003471 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003472 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003473 }
Mike Klein16885072018-12-11 09:54:31 -05003474 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003475 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003476}
3477
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003478GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3479 SkScalar rotate = random->nextSScalar1() * 360.f;
3480 SkScalar translateX = random->nextSScalar1() * 1000.f;
3481 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003482 SkScalar scale;
3483 do {
3484 scale = random->nextSScalar1() * 100.f;
3485 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003486 SkMatrix viewMatrix;
3487 viewMatrix.setRotate(rotate);
3488 viewMatrix.postTranslate(translateX, translateY);
3489 viewMatrix.postScale(scale, scale);
3490 SkRect circle = GrTest::TestSquare(random);
3491 SkPoint center = {circle.centerX(), circle.centerY()};
3492 SkScalar radius = circle.width() / 2.f;
3493 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3494 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3495 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3496 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3497 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003498 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3499 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003500 startAngle, onAngle, offAngle, phase);
3501}
3502
Brian Salomon05441c42017-05-15 16:45:49 -04003503GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003504 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003505 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003506 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003507 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003508}
3509
Brian Salomon05441c42017-05-15 16:45:49 -04003510GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003511 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003512 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003513 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003514 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003515}
3516
Jim Van Verth64b85892019-06-17 12:01:46 -04003517GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3518 do {
3519 SkScalar rotate = random->nextSScalar1() * 360.f;
3520 SkScalar translateX = random->nextSScalar1() * 1000.f;
3521 SkScalar translateY = random->nextSScalar1() * 1000.f;
3522 SkScalar scale;
3523 do {
3524 scale = random->nextSScalar1() * 100.f;
3525 } while (scale == 0);
3526 SkMatrix viewMatrix;
3527 viewMatrix.setRotate(rotate);
3528 viewMatrix.postTranslate(translateX, translateY);
3529 viewMatrix.postScale(scale, scale);
3530 SkRect rect = GrTest::TestRect(random);
3531 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3532 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3533 if (rrect.isOval()) {
3534 continue;
3535 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003536 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003537 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3538 GrTest::TestStrokeRec(random), nullptr);
3539 if (op) {
3540 return op;
3541 }
3542 assert_alive(paint);
3543 } while (true);
3544}
3545
Brian Salomon05441c42017-05-15 16:45:49 -04003546GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003547 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003548 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003549 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003550 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003551}
3552
3553#endif