blob: e5ab4934b27644d01b236623e57ffaeef7b7b04a [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) {
724 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
725 d->fRandom->nextBool(), d->fRandom->nextBool(),
726 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000727}
Hal Canary6f6961e2017-01-31 13:50:44 -0500728#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000729
730///////////////////////////////////////////////////////////////////////////////
731
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000732/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000733 * 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 +0000734 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
735 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
736 * using differentials.
737 *
738 * The result is device-independent and can be used with any affine matrix.
739 */
740
bsalomoncdaa97b2016-03-08 08:30:14 -0800741enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000742
bsalomoncdaa97b2016-03-08 08:30:14 -0800743class DIEllipseGeometryProcessor : public GrGeometryProcessor {
744public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500745 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
746 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600747 return arena->make([&](void* ptr) {
748 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
749 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500750 }
751
752 ~DIEllipseGeometryProcessor() override {}
753
754 const char* name() const override { return "DIEllipseGeometryProcessor"; }
755
756 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
757 GLSLProcessor::GenKey(*this, caps, b);
758 }
759
Robert Phillipsf10535f2021-03-23 09:30:45 -0400760 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500761 return new GLSLProcessor();
762 }
763
764private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000765 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400766 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400767 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400768 , fViewMatrix(viewMatrix)
769 , fUseScale(useScale)
770 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500771 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500772 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400773 if (useScale) {
774 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
775 kFloat3_GrSLType};
776 } else {
777 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
778 kFloat2_GrSLType};
779 }
780 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500781 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000782 }
783
egdaniel57d3b032015-11-13 11:57:27 -0800784 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000785 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500786 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000787
joshualitt465283c2015-09-11 08:19:35 -0700788 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400789 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800790 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800791 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800792 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793
joshualittabb52a12015-01-13 15:02:10 -0800794 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800795 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800796
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400797 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
798 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800799 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500800 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700801
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400802 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800803 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500804 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800805
Chris Dalton60283612018-02-14 13:38:14 -0700806 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500807 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500808 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800809
joshualittabb52a12015-01-13 15:02:10 -0800810 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400811 WriteOutputPosition(vertBuilder,
812 uniformHandler,
813 *args.fShaderCaps,
814 gpArgs,
815 diegp.fInPosition.name(),
816 diegp.fViewMatrix,
817 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400818 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800819
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000820 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400821 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
822 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
823 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
824 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500825 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400826 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
827 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500828 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400829 if (diegp.fUseScale) {
830 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
831 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000832
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000834 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 if (args.fShaderCaps->floatIs32Bits()) {
836 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
837 } else {
838 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
839 }
840 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
841 if (diegp.fUseScale) {
842 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
843 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800844 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000845 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000846 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
847 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000848 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000849 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 }
851
852 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800853 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800854 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
855 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400856 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
857 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500858 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400859 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
860 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500861 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400862 if (diegp.fUseScale) {
863 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
864 }
865 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
866 if (!args.fShaderCaps->floatIs32Bits()) {
867 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
868 }
869 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
870 if (diegp.fUseScale) {
871 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
872 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000873 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000874 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000875
John Stiles4d7ac492021-03-09 20:16:43 -0500876 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000877 }
878
robertphillips46d36f02015-01-18 08:14:14 -0800879 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400880 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700881 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800882 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500883 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
Brian Salomon5a328282021-04-14 10:32:25 -0400884 b->addBits(kMatrixKeyBits,
885 ComputeMatrixKey(shaderCaps, diegp.fViewMatrix),
886 "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000887 }
888
Brian Osman609f1592020-07-01 15:14:39 -0400889 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400890 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400891 const GrGeometryProcessor& geomProc) override {
892 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700893
Brian Salomon5a328282021-04-14 10:32:25 -0400894 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000895 }
896
897 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400898 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700899 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800900
John Stiles7571f9e2020-09-02 22:42:33 -0400901 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000902 };
903
Brian Salomon92be2f72018-06-19 14:33:47 -0400904
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500905 Attribute fInPosition;
906 Attribute fInColor;
907 Attribute fInEllipseOffsets0;
908 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400909
Brian Salomon289e3d82016-12-14 15:52:56 -0500910 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400911 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500912 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000913
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400914 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915
John Stiles7571f9e2020-09-02 22:42:33 -0400916 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000917};
918
bsalomoncdaa97b2016-03-08 08:30:14 -0800919GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000920
Hal Canary6f6961e2017-01-31 13:50:44 -0500921#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500922GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
923 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
924 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
925 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000926}
Hal Canary6f6961e2017-01-31 13:50:44 -0500927#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000928
929///////////////////////////////////////////////////////////////////////////////
930
jvanverth6ca48822016-10-07 06:57:32 -0700931// We have two possible cases for geometry for a circle:
932
933// In the case of a normal fill, we draw geometry for the circle as an octagon.
934static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500935 // enter the octagon
936 // clang-format off
937 0, 1, 8, 1, 2, 8,
938 2, 3, 8, 3, 4, 8,
939 4, 5, 8, 5, 6, 8,
940 6, 7, 8, 7, 0, 8
941 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700942};
943
944// For stroked circles, we use two nested octagons.
945static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500946 // enter the octagon
947 // clang-format off
948 0, 1, 9, 0, 9, 8,
949 1, 2, 10, 1, 10, 9,
950 2, 3, 11, 2, 11, 10,
951 3, 4, 12, 3, 12, 11,
952 4, 5, 13, 4, 13, 12,
953 5, 6, 14, 5, 14, 13,
954 6, 7, 15, 6, 15, 14,
955 7, 0, 8, 7, 8, 15,
956 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700957};
958
Brian Osman9d958b52018-11-13 12:46:56 -0500959// Normalized geometry for octagons that circumscribe and lie on a circle:
960
961static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
962static constexpr SkPoint kOctagonOuter[] = {
963 SkPoint::Make(-kOctOffset, -1),
964 SkPoint::Make( kOctOffset, -1),
965 SkPoint::Make( 1, -kOctOffset),
966 SkPoint::Make( 1, kOctOffset),
967 SkPoint::Make( kOctOffset, 1),
968 SkPoint::Make(-kOctOffset, 1),
969 SkPoint::Make(-1, kOctOffset),
970 SkPoint::Make(-1, -kOctOffset),
971};
972
973// cosine and sine of pi/8
974static constexpr SkScalar kCosPi8 = 0.923579533f;
975static constexpr SkScalar kSinPi8 = 0.382683432f;
976static constexpr SkPoint kOctagonInner[] = {
977 SkPoint::Make(-kSinPi8, -kCosPi8),
978 SkPoint::Make( kSinPi8, -kCosPi8),
979 SkPoint::Make( kCosPi8, -kSinPi8),
980 SkPoint::Make( kCosPi8, kSinPi8),
981 SkPoint::Make( kSinPi8, kCosPi8),
982 SkPoint::Make(-kSinPi8, kCosPi8),
983 SkPoint::Make(-kCosPi8, kSinPi8),
984 SkPoint::Make(-kCosPi8, -kSinPi8),
985};
Brian Salomon289e3d82016-12-14 15:52:56 -0500986
jvanverth6ca48822016-10-07 06:57:32 -0700987static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
988static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
989static const int kVertsPerStrokeCircle = 16;
990static const int kVertsPerFillCircle = 9;
991
992static int circle_type_to_vert_count(bool stroked) {
993 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
994}
995
996static int circle_type_to_index_count(bool stroked) {
997 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
998}
999
1000static const uint16_t* circle_type_to_indices(bool stroked) {
1001 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1002}
1003
1004///////////////////////////////////////////////////////////////////////////////
1005
Brian Salomon05441c42017-05-15 16:45:49 -04001006class CircleOp final : public GrMeshDrawOp {
1007private:
1008 using Helper = GrSimpleMeshDrawOpHelper;
1009
joshualitt76e7fb62015-02-11 08:52:27 -08001010public:
Brian Salomon25a88092016-12-01 09:36:50 -05001011 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001012
bsalomon4f3a0ca2016-08-22 13:14:26 -07001013 /** Optional extra params to render a partial arc rather than a full circle. */
1014 struct ArcParams {
1015 SkScalar fStartAngleRadians;
1016 SkScalar fSweepAngleRadians;
1017 bool fUseCenter;
1018 };
Brian Salomon05441c42017-05-15 16:45:49 -04001019
Herb Derbyc76d4092020-10-07 16:46:15 -04001020 static GrOp::Owner Make(GrRecordingContext* context,
1021 GrPaint&& paint,
1022 const SkMatrix& viewMatrix,
1023 SkPoint center,
1024 SkScalar radius,
1025 const GrStyle& style,
1026 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001027 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001028 if (style.hasPathEffect()) {
1029 return nullptr;
1030 }
Brian Salomon05441c42017-05-15 16:45:49 -04001031 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001032 SkStrokeRec::Style recStyle = stroke.getStyle();
1033 if (arcParams) {
1034 // Arc support depends on the style.
1035 switch (recStyle) {
1036 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001037 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001038 return nullptr;
1039 case SkStrokeRec::kFill_Style:
1040 // This supports all fills.
1041 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001042 case SkStrokeRec::kStroke_Style:
1043 // Strokes that don't use the center point are supported with butt and round
1044 // caps.
1045 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1046 return nullptr;
1047 }
1048 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001050 // Hairline only supports butt cap. Round caps could be emulated by slightly
1051 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001052 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1053 return nullptr;
1054 }
1055 break;
1056 }
1057 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001058 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1059 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001060 }
1061
Herb Derbyc76d4092020-10-07 16:46:15 -04001062 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001063 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1064 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001065 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001066 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001067 const SkStrokeRec& stroke = style.strokeRec();
1068 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001069
Brian Salomon45c92202018-04-10 10:53:58 -04001070 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001071
bsalomon4b4a7cc2016-07-08 04:42:54 -07001072 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001074 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001075
Brian Salomon289e3d82016-12-14 15:52:56 -05001076 bool isStrokeOnly =
1077 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001079
jvanverth6ca48822016-10-07 06:57:32 -07001080 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001081 SkScalar outerRadius = radius;
1082 SkScalar halfWidth = 0;
1083 if (hasStroke) {
1084 if (SkScalarNearlyZero(strokeWidth)) {
1085 halfWidth = SK_ScalarHalf;
1086 } else {
1087 halfWidth = SkScalarHalf(strokeWidth);
1088 }
1089
1090 outerRadius += halfWidth;
1091 if (isStrokeOnly) {
1092 innerRadius = radius - halfWidth;
1093 }
1094 }
1095
1096 // The radii are outset for two reasons. First, it allows the shader to simply perform
1097 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1098 // Second, the outer radius is used to compute the verts of the bounding box that is
1099 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001100 outerRadius += SK_ScalarHalf;
1101 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001102 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001103 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001104
bsalomon4f3a0ca2016-08-22 13:14:26 -07001105 // This makes every point fully inside the intersection plane.
1106 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1107 // This makes every point fully outside the union plane.
1108 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001109 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1111 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001112 if (arcParams) {
1113 // The shader operates in a space where the circle is translated to be centered at the
1114 // origin. Here we compute points on the unit circle at the starting and ending angles.
1115 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001116 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1117 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001119 stopPoint.fY = SkScalarSin(endAngle);
1120 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001121
1122 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1123 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1124 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1125 startPoint.normalize();
1126 stopPoint.normalize();
1127
Brian Salomon3517aa72019-12-11 08:16:22 -05001128 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1129 // should orient the clip planes for arcs.
1130 SkASSERT(viewMatrix.isSimilarity());
1131 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1132 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1133 if (upperLeftDet < 0) {
1134 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001135 }
1136
Brian Salomon45c92202018-04-10 10:53:58 -04001137 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1138 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1139 SkPoint roundCaps[2];
1140 if (fRoundCaps) {
1141 // Compute the cap center points in the normalized space.
1142 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1143 roundCaps[0] = startPoint * midRadius;
1144 roundCaps[1] = stopPoint * midRadius;
1145 } else {
1146 roundCaps[0] = kUnusedRoundCaps[0];
1147 roundCaps[1] = kUnusedRoundCaps[1];
1148 }
1149
bsalomon4f3a0ca2016-08-22 13:14:26 -07001150 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001151 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1152 // center of the butts.
1153 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001155 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001156 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001157 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1158 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1159 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001160 if (useCenter) {
1161 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1162 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001163 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1164 if (arcParams->fSweepAngleRadians < 0) {
1165 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001166 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001167 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001168 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001169 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001170 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001171 color,
1172 innerRadius,
1173 outerRadius,
1174 {norm0.fX, norm0.fY, 0.5f},
1175 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1176 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001177 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001178 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001179 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001180 fClipPlaneIsect = false;
1181 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001182 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001183 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001184 color,
1185 innerRadius,
1186 outerRadius,
1187 {norm0.fX, norm0.fY, 0.5f},
1188 {norm1.fX, norm1.fY, 0.5f},
1189 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001190 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001191 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001192 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fClipPlaneIsect = true;
1194 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 }
1196 } else {
1197 // We clip to a secant of the original circle.
1198 startPoint.scale(radius);
1199 stopPoint.scale(radius);
1200 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1201 norm.normalize();
1202 if (arcParams->fSweepAngleRadians > 0) {
1203 norm.negate();
1204 }
1205 SkScalar d = -norm.dot(startPoint) + 0.5f;
1206
Brian Salomon05441c42017-05-15 16:45:49 -04001207 fCircles.emplace_back(
1208 Circle{color,
1209 innerRadius,
1210 outerRadius,
1211 {norm.fX, norm.fY, d},
1212 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1213 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001214 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001215 devBounds,
1216 stroked});
1217 fClipPlane = true;
1218 fClipPlaneIsect = false;
1219 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001220 }
1221 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001222 fCircles.emplace_back(
1223 Circle{color,
1224 innerRadius,
1225 outerRadius,
1226 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1227 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1228 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001229 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001230 devBounds,
1231 stroked});
1232 fClipPlane = false;
1233 fClipPlaneIsect = false;
1234 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001235 }
bsalomon88cf17d2016-07-08 06:40:56 -07001236 // Use the original radius and stroke radius for the bounds so that it does not include the
1237 // AA bloat.
1238 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001239 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001240 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001241 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001242 fVertCount = circle_type_to_vert_count(stroked);
1243 fIndexCount = circle_type_to_index_count(stroked);
1244 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001245 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001246
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001248
Chris Dalton1706cbf2019-05-21 19:35:29 -06001249 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001250 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001251 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001252 } else {
1253 fHelper.visitProxies(func);
1254 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001255 }
1256
Chris Dalton57ab06c2021-04-22 12:57:28 -06001257 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1258 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001259 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001260 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001261 GrProcessorAnalysisCoverage::kSingleChannel, color,
1262 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001263 }
1264
1265 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1266
bsalomone46f9fe2015-08-18 06:05:14 -07001267private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001268 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001269
Robert Phillips4133dc42020-03-11 15:55:55 -04001270 void onCreateProgramInfo(const GrCaps* caps,
1271 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001272 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001273 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001274 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001275 GrXferBarrierFlags renderPassXferBarriers,
1276 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001277 SkMatrix localMatrix;
1278 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001279 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001280 }
1281
Robert Phillips4490d922020-03-03 14:50:59 -05001282 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001283 fClipPlaneIsect, fClipPlaneUnion,
1284 fRoundCaps, fWideColor,
1285 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001286
Brian Salomon8afde5f2020-04-01 16:22:00 -04001287 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001288 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001289 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001290 }
1291
Robert Phillips4490d922020-03-03 14:50:59 -05001292 void onPrepareDraws(Target* target) override {
1293 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001294 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001295 if (!fProgramInfo) {
1296 return;
1297 }
1298 }
1299
Brian Salomon12d22642019-01-29 14:38:50 -05001300 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001301 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001302 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001303 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001304 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001305 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001306 return;
1307 }
1308
Brian Salomon12d22642019-01-29 14:38:50 -05001309 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001310 int firstIndex = 0;
1311 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1312 if (!indices) {
1313 SkDebugf("Could not allocate indices\n");
1314 return;
1315 }
1316
1317 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001318 for (const auto& circle : fCircles) {
1319 SkScalar innerRadius = circle.fInnerRadius;
1320 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001321 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001322 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001323
joshualitt76e7fb62015-02-11 08:52:27 -08001324 // The inner radius in the vertex data must be specified in normalized space.
1325 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001326 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001327
1328 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001329 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001330
Brian Osman9a24fee2018-08-03 09:48:42 -04001331 SkVector geoClipPlane = { 0, 0 };
1332 SkScalar offsetClipDist = SK_Scalar1;
1333 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1334 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1335 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1336 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1337 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1338 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1339 // the AA can extend just past the center of the circle.
1340 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1341 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1342 SkAssertResult(geoClipPlane.normalize());
1343 offsetClipDist = 0.5f / halfWidth;
1344 }
1345
Brian Osman7d8f82b2018-11-08 10:24:09 -05001346 for (int i = 0; i < 8; ++i) {
1347 // This clips the normalized offset to the half-plane we computed above. Then we
1348 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001349 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001350 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001351 vertices.write(center + offset * halfWidth,
1352 color,
1353 offset,
1354 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001355 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 }
1358 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001359 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001360 }
1361 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001362 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001363 }
1364 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001366 }
Brian Salomon45c92202018-04-10 10:53:58 -04001367 }
jvanverth6ca48822016-10-07 06:57:32 -07001368
Brian Salomon05441c42017-05-15 16:45:49 -04001369 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001370 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001371
Brian Osman7d8f82b2018-11-08 10:24:09 -05001372 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001373 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1374 color,
1375 kOctagonInner[i] * innerRadius,
1376 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001377 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001378 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001379 }
1380 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001381 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001382 }
1383 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001384 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001385 }
1386 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001387 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001388 }
Brian Salomon45c92202018-04-10 10:53:58 -04001389 }
jvanverth6ca48822016-10-07 06:57:32 -07001390 } else {
1391 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001392 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001393 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001394 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001395 }
jvanverth6ca48822016-10-07 06:57:32 -07001396 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001397 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001398 }
1399 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001400 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001401 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001402 if (fRoundCaps) {
1403 vertices.write(circle.fRoundCapCenters);
1404 }
jvanverth6ca48822016-10-07 06:57:32 -07001405 }
1406
Brian Salomon05441c42017-05-15 16:45:49 -04001407 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1408 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001409 for (int i = 0; i < primIndexCount; ++i) {
1410 *indices++ = primIndices[i] + currStartVertex;
1411 }
1412
Brian Salomon05441c42017-05-15 16:45:49 -04001413 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001414 }
jvanverth6ca48822016-10-07 06:57:32 -07001415
Robert Phillips4490d922020-03-03 14:50:59 -05001416 fMesh = target->allocMesh();
1417 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001418 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001419 }
1420
1421 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001422 if (!fProgramInfo || !fMesh) {
1423 return;
1424 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001425
Chris Dalton765ed362020-03-16 17:34:44 -06001426 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001427 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001428 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001429 }
1430
Herb Derbye25c3002020-10-27 15:57:27 -04001431 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001432 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001433
1434 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001435 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001436 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001437 }
1438
Brian Salomon05441c42017-05-15 16:45:49 -04001439 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001440 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001441 }
1442
Brian Salomon05441c42017-05-15 16:45:49 -04001443 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001444 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1445 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001446 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001447 }
1448
Brian Salomon289e3d82016-12-14 15:52:56 -05001449 // Because we've set up the ops that don't use the planes with noop values
1450 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001451 fClipPlane |= that->fClipPlane;
1452 fClipPlaneIsect |= that->fClipPlaneIsect;
1453 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001454 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001455 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001456
Brian Salomon05441c42017-05-15 16:45:49 -04001457 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001458 fVertCount += that->fVertCount;
1459 fIndexCount += that->fIndexCount;
1460 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001461 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001462 }
1463
John Stilesaf366522020-08-13 09:57:34 -04001464#if GR_TEST_UTILS
1465 SkString onDumpInfo() const override {
1466 SkString string;
1467 for (int i = 0; i < fCircles.count(); ++i) {
1468 string.appendf(
1469 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1470 "InnerRad: %.2f, OuterRad: %.2f\n",
1471 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1472 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1473 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1474 fCircles[i].fOuterRadius);
1475 }
1476 string += fHelper.dumpInfo();
1477 return string;
1478 }
1479#endif
1480
Brian Salomon05441c42017-05-15 16:45:49 -04001481 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001482 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001483 SkScalar fInnerRadius;
1484 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001485 SkScalar fClipPlane[3];
1486 SkScalar fIsectPlane[3];
1487 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001488 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001489 SkRect fDevBounds;
1490 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001491 };
1492
Brian Salomon289e3d82016-12-14 15:52:56 -05001493 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001494 Helper fHelper;
1495 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001496 int fVertCount;
1497 int fIndexCount;
1498 bool fAllFill;
1499 bool fClipPlane;
1500 bool fClipPlaneIsect;
1501 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001502 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001503 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001504
Chris Daltoneb694b72020-03-16 09:25:50 -06001505 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001506 GrProgramInfo* fProgramInfo = nullptr;
1507
John Stiles7571f9e2020-09-02 22:42:33 -04001508 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001509};
1510
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001511class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1512private:
1513 using Helper = GrSimpleMeshDrawOpHelper;
1514
1515public:
1516 DEFINE_OP_CLASS_ID
1517
Herb Derbyc76d4092020-10-07 16:46:15 -04001518 static GrOp::Owner Make(GrRecordingContext* context,
1519 GrPaint&& paint,
1520 const SkMatrix& viewMatrix,
1521 SkPoint center,
1522 SkScalar radius,
1523 SkScalar strokeWidth,
1524 SkScalar startAngle,
1525 SkScalar onAngle,
1526 SkScalar offAngle,
1527 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001528 SkASSERT(circle_stays_circle(viewMatrix));
1529 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001530 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1531 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001532 onAngle, offAngle, phaseAngle);
1533 }
1534
Herb Derbyc76d4092020-10-07 16:46:15 -04001535 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001536 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1537 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1538 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001539 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001540 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001541 SkASSERT(circle_stays_circle(viewMatrix));
1542 viewMatrix.mapPoints(&center, 1);
1543 radius = viewMatrix.mapRadius(radius);
1544 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1545
1546 // Determine the angle where the circle starts in device space and whether its orientation
1547 // has been reversed.
1548 SkVector start;
1549 bool reflection;
1550 if (!startAngle) {
1551 start = {1, 0};
1552 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001553 start.fY = SkScalarSin(startAngle);
1554 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001555 }
1556 viewMatrix.mapVectors(&start, 1);
1557 startAngle = SkScalarATan2(start.fY, start.fX);
1558 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1559 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1560
1561 auto totalAngle = onAngle + offAngle;
1562 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1563
1564 SkScalar halfWidth = 0;
1565 if (SkScalarNearlyZero(strokeWidth)) {
1566 halfWidth = SK_ScalarHalf;
1567 } else {
1568 halfWidth = SkScalarHalf(strokeWidth);
1569 }
1570
1571 SkScalar outerRadius = radius + halfWidth;
1572 SkScalar innerRadius = radius - halfWidth;
1573
1574 // The radii are outset for two reasons. First, it allows the shader to simply perform
1575 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1576 // Second, the outer radius is used to compute the verts of the bounding box that is
1577 // rendered and the outset ensures the box will cover all partially covered by the circle.
1578 outerRadius += SK_ScalarHalf;
1579 innerRadius -= SK_ScalarHalf;
1580 fViewMatrixIfUsingLocalCoords = viewMatrix;
1581
1582 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1583 center.fX + outerRadius, center.fY + outerRadius);
1584
1585 // We store whether there is a reflection as a negative total angle.
1586 if (reflection) {
1587 totalAngle = -totalAngle;
1588 }
1589 fCircles.push_back(Circle{
1590 color,
1591 outerRadius,
1592 innerRadius,
1593 onAngle,
1594 totalAngle,
1595 startAngle,
1596 phaseAngle,
1597 devBounds
1598 });
1599 // Use the original radius and stroke radius for the bounds so that it does not include the
1600 // AA bloat.
1601 radius += halfWidth;
1602 this->setBounds(
1603 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001604 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001605 fVertCount = circle_type_to_vert_count(true);
1606 fIndexCount = circle_type_to_index_count(true);
1607 }
1608
1609 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1610
Chris Dalton1706cbf2019-05-21 19:35:29 -06001611 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001612 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001613 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001614 } else {
1615 fHelper.visitProxies(func);
1616 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001617 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001618
Chris Dalton57ab06c2021-04-22 12:57:28 -06001619 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1620 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001621 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001622 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001623 GrProcessorAnalysisCoverage::kSingleChannel, color,
1624 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001625 }
1626
1627 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1628
1629private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001630 GrProgramInfo* programInfo() override { return fProgramInfo; }
1631
Robert Phillips4133dc42020-03-11 15:55:55 -04001632 void onCreateProgramInfo(const GrCaps* caps,
1633 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001634 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001635 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001636 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001637 GrXferBarrierFlags renderPassXferBarriers,
1638 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001639 SkMatrix localMatrix;
1640 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001641 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001642 }
1643
1644 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001645 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001646 fWideColor,
1647 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001648
Brian Salomon8afde5f2020-04-01 16:22:00 -04001649 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001650 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001651 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001652 }
1653
Robert Phillips4490d922020-03-03 14:50:59 -05001654 void onPrepareDraws(Target* target) override {
1655 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001656 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001657 if (!fProgramInfo) {
1658 return;
1659 }
1660 }
1661
Brian Salomon12d22642019-01-29 14:38:50 -05001662 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001663 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001664 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001665 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001666 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 SkDebugf("Could not allocate vertices\n");
1668 return;
1669 }
1670
Brian Salomon12d22642019-01-29 14:38:50 -05001671 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001672 int firstIndex = 0;
1673 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1674 if (!indices) {
1675 SkDebugf("Could not allocate indices\n");
1676 return;
1677 }
1678
1679 int currStartVertex = 0;
1680 for (const auto& circle : fCircles) {
1681 // The inner radius in the vertex data must be specified in normalized space so that
1682 // length() can be called with smaller values to avoid precision issues with half
1683 // floats.
1684 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1685 const SkRect& bounds = circle.fDevBounds;
1686 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001687 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1688 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1689 };
1690 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001691 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001692 dashParams.totalAngle = -dashParams.totalAngle;
1693 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001694 }
1695
Brian Osmane3caf2d2018-11-21 13:48:36 -05001696 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001697
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001698 // The bounding geometry for the circle is composed of an outer bounding octagon and
1699 // an inner bounded octagon.
1700
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001701 // Compute the vertices of the outer octagon.
1702 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1703 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001704
1705 auto reflectY = [=](const SkPoint& p) {
1706 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001707 };
Brian Osman9d958b52018-11-13 12:46:56 -05001708
1709 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001710 vertices.write(center + kOctagonOuter[i] * halfWidth,
1711 color,
1712 reflectY(kOctagonOuter[i]),
1713 circle.fOuterRadius,
1714 normInnerRadius,
1715 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001716 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001717
1718 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001719 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001720 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1721 color,
1722 reflectY(kOctagonInner[i]) * normInnerRadius,
1723 circle.fOuterRadius,
1724 normInnerRadius,
1725 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001726 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001727
1728 const uint16_t* primIndices = circle_type_to_indices(true);
1729 const int primIndexCount = circle_type_to_index_count(true);
1730 for (int i = 0; i < primIndexCount; ++i) {
1731 *indices++ = primIndices[i] + currStartVertex;
1732 }
1733
1734 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001735 }
1736
Robert Phillips4490d922020-03-03 14:50:59 -05001737 fMesh = target->allocMesh();
1738 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001739 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001740 }
1741
1742 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001743 if (!fProgramInfo || !fMesh) {
1744 return;
1745 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001746
Chris Dalton765ed362020-03-16 17:34:44 -06001747 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001748 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001749 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001750 }
1751
Herb Derbye25c3002020-10-27 15:57:27 -04001752 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001753 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1754
1755 // can only represent 65535 unique vertices with 16-bit indices
1756 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001757 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001758 }
1759
1760 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001761 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001762 }
1763
1764 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001765 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1766 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001767 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001768 }
1769
1770 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001771 fVertCount += that->fVertCount;
1772 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001773 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001774 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001775 }
1776
John Stilesaf366522020-08-13 09:57:34 -04001777#if GR_TEST_UTILS
1778 SkString onDumpInfo() const override {
1779 SkString string;
1780 for (int i = 0; i < fCircles.count(); ++i) {
1781 string.appendf(
1782 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1783 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1784 "Phase: %.2f\n",
1785 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1786 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1787 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1788 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1789 fCircles[i].fPhaseAngle);
1790 }
1791 string += fHelper.dumpInfo();
1792 return string;
1793 }
1794#endif
1795
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001796 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001797 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001798 SkScalar fOuterRadius;
1799 SkScalar fInnerRadius;
1800 SkScalar fOnAngle;
1801 SkScalar fTotalAngle;
1802 SkScalar fStartAngle;
1803 SkScalar fPhaseAngle;
1804 SkRect fDevBounds;
1805 };
1806
1807 SkMatrix fViewMatrixIfUsingLocalCoords;
1808 Helper fHelper;
1809 SkSTArray<1, Circle, true> fCircles;
1810 int fVertCount;
1811 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001812 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001813
Chris Daltoneb694b72020-03-16 09:25:50 -06001814 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001815 GrProgramInfo* fProgramInfo = nullptr;
1816
John Stiles7571f9e2020-09-02 22:42:33 -04001817 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001818};
1819
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001820///////////////////////////////////////////////////////////////////////////////
1821
Brian Salomon05441c42017-05-15 16:45:49 -04001822class EllipseOp : public GrMeshDrawOp {
1823private:
1824 using Helper = GrSimpleMeshDrawOpHelper;
1825
1826 struct DeviceSpaceParams {
1827 SkPoint fCenter;
1828 SkScalar fXRadius;
1829 SkScalar fYRadius;
1830 SkScalar fInnerXRadius;
1831 SkScalar fInnerYRadius;
1832 };
1833
joshualitt76e7fb62015-02-11 08:52:27 -08001834public:
Brian Salomon25a88092016-12-01 09:36:50 -05001835 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001836
Herb Derbyc76d4092020-10-07 16:46:15 -04001837 static GrOp::Owner Make(GrRecordingContext* context,
1838 GrPaint&& paint,
1839 const SkMatrix& viewMatrix,
1840 const SkRect& ellipse,
1841 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001842 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001843 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001844 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1845 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001846 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1847 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001848 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1849 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1850 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1851 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001852
bsalomon4b4a7cc2016-07-08 04:42:54 -07001853 // do (potentially) anisotropic mapping of stroke
1854 SkVector scaledStroke;
1855 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001856 scaledStroke.fX = SkScalarAbs(
1857 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1858 scaledStroke.fY = SkScalarAbs(
1859 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001860
1861 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001862 bool isStrokeOnly =
1863 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001864 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1865
Brian Salomon05441c42017-05-15 16:45:49 -04001866 params.fInnerXRadius = 0;
1867 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001868 if (hasStroke) {
1869 if (SkScalarNearlyZero(scaledStroke.length())) {
1870 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1871 } else {
1872 scaledStroke.scale(SK_ScalarHalf);
1873 }
1874
1875 // we only handle thick strokes for near-circular ellipses
1876 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001877 (0.5f * params.fXRadius > params.fYRadius ||
1878 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001879 return nullptr;
1880 }
1881
1882 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001883 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1884 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1885 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1886 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001887 return nullptr;
1888 }
1889
1890 // this is legit only if scale & translation (which should be the case at the moment)
1891 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001892 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1893 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001894 }
1895
Brian Salomon05441c42017-05-15 16:45:49 -04001896 params.fXRadius += scaledStroke.fX;
1897 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001898 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001899
1900 // For large ovals with low precision floats, we fall back to the path renderer.
1901 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1902 // minimum value to avoid divides by zero. With large ovals and low precision this
1903 // leads to blurring at the edge of the oval.
1904 const SkScalar kMaxOvalRadius = 16384;
1905 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1906 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1907 return nullptr;
1908 }
1909
Greg Daniel2655ede2019-04-10 00:49:28 +00001910 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001911 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001912 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001913
Herb Derbyc76d4092020-10-07 16:46:15 -04001914 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001915 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001916 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001917 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001918 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001919 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001920 SkStrokeRec::Style style = stroke.getStyle();
1921 bool isStrokeOnly =
1922 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001923
Brian Salomon05441c42017-05-15 16:45:49 -04001924 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1925 params.fInnerXRadius, params.fInnerYRadius,
1926 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1927 params.fCenter.fY - params.fYRadius,
1928 params.fCenter.fX + params.fXRadius,
1929 params.fCenter.fY + params.fYRadius)});
1930
Greg Daniel5faf4742019-10-01 15:14:44 -04001931 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001932
bsalomon4b4a7cc2016-07-08 04:42:54 -07001933 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001934 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001935
Brian Salomon05441c42017-05-15 16:45:49 -04001936 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1937 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001938 }
joshualitt76e7fb62015-02-11 08:52:27 -08001939
Brian Salomon289e3d82016-12-14 15:52:56 -05001940 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001941
Chris Dalton1706cbf2019-05-21 19:35:29 -06001942 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001943 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001944 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001945 } else {
1946 fHelper.visitProxies(func);
1947 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001948 }
1949
Chris Dalton57ab06c2021-04-22 12:57:28 -06001950 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1951 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001952 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1953 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001954 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001955 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001956 GrProcessorAnalysisCoverage::kSingleChannel, color,
1957 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001958 }
1959
1960 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1961
bsalomone46f9fe2015-08-18 06:05:14 -07001962private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001963 GrProgramInfo* programInfo() override { return fProgramInfo; }
1964
Robert Phillips4133dc42020-03-11 15:55:55 -04001965 void onCreateProgramInfo(const GrCaps* caps,
1966 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001967 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001968 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001969 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001970 GrXferBarrierFlags renderPassXferBarriers,
1971 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001972 SkMatrix localMatrix;
1973 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001974 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001975 }
1976
Robert Phillips4490d922020-03-03 14:50:59 -05001977 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1978 fUseScale, localMatrix);
1979
Brian Salomon8afde5f2020-04-01 16:22:00 -04001980 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001981 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001982 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001983 }
1984
Robert Phillips4490d922020-03-03 14:50:59 -05001985 void onPrepareDraws(Target* target) override {
1986 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001987 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001988 if (!fProgramInfo) {
1989 return;
1990 }
1991 }
1992
Robert Phillips787fd9d2021-03-22 14:48:09 -04001993 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001994 GrVertexWriter verts{helper.vertices()};
1995 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001996 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001997 return;
1998 }
1999
Brian Salomon05441c42017-05-15 16:45:49 -04002000 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002001 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002002 SkScalar xRadius = ellipse.fXRadius;
2003 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002004
2005 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002006 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2007 SkScalarInvert(xRadius),
2008 SkScalarInvert(yRadius),
2009 SkScalarInvert(ellipse.fInnerXRadius),
2010 SkScalarInvert(ellipse.fInnerYRadius)
2011 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002012 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2013 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002014
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002015 if (!fStroked) {
2016 // For filled ellipses we map a unit circle in the vertex attributes rather than
2017 // computing an ellipse and modifying that distance, so we normalize to 1
2018 xMaxOffset /= xRadius;
2019 yMaxOffset /= yRadius;
2020 }
2021
joshualitt76e7fb62015-02-11 08:52:27 -08002022 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002023 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2024 color,
2025 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002026 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002027 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002028 }
Robert Phillips4490d922020-03-03 14:50:59 -05002029 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002030 }
2031
2032 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002033 if (!fProgramInfo || !fMesh) {
2034 return;
2035 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002036
Chris Dalton765ed362020-03-16 17:34:44 -06002037 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002038 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002039 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002040 }
2041
Herb Derbye25c3002020-10-27 15:57:27 -04002042 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002043 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002044
Brian Salomon05441c42017-05-15 16:45:49 -04002045 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002046 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002047 }
2048
bsalomoncdaa97b2016-03-08 08:30:14 -08002049 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002050 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002051 }
2052
Brian Salomon05441c42017-05-15 16:45:49 -04002053 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002054 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2055 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002056 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002057 }
2058
Brian Salomon05441c42017-05-15 16:45:49 -04002059 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002060 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002061 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002062 }
2063
John Stilesaf366522020-08-13 09:57:34 -04002064#if GR_TEST_UTILS
2065 SkString onDumpInfo() const override {
2066 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2067 for (const auto& geo : fEllipses) {
2068 string.appendf(
2069 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2070 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2071 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2072 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2073 geo.fInnerXRadius, geo.fInnerYRadius);
2074 }
2075 string += fHelper.dumpInfo();
2076 return string;
2077 }
2078#endif
2079
Brian Salomon05441c42017-05-15 16:45:49 -04002080 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002081 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002082 SkScalar fXRadius;
2083 SkScalar fYRadius;
2084 SkScalar fInnerXRadius;
2085 SkScalar fInnerYRadius;
2086 SkRect fDevBounds;
2087 };
joshualitt76e7fb62015-02-11 08:52:27 -08002088
Brian Salomon289e3d82016-12-14 15:52:56 -05002089 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002090 Helper fHelper;
2091 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002092 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002093 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002094 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002095
Chris Daltoneb694b72020-03-16 09:25:50 -06002096 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002097 GrProgramInfo* fProgramInfo = nullptr;
2098
John Stiles7571f9e2020-09-02 22:42:33 -04002099 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002100};
2101
joshualitt76e7fb62015-02-11 08:52:27 -08002102/////////////////////////////////////////////////////////////////////////////////////////////////
2103
Brian Salomon05441c42017-05-15 16:45:49 -04002104class DIEllipseOp : public GrMeshDrawOp {
2105private:
2106 using Helper = GrSimpleMeshDrawOpHelper;
2107
2108 struct DeviceSpaceParams {
2109 SkPoint fCenter;
2110 SkScalar fXRadius;
2111 SkScalar fYRadius;
2112 SkScalar fInnerXRadius;
2113 SkScalar fInnerYRadius;
2114 DIEllipseStyle fStyle;
2115 };
2116
joshualitt76e7fb62015-02-11 08:52:27 -08002117public:
Brian Salomon25a88092016-12-01 09:36:50 -05002118 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002119
Herb Derbyc76d4092020-10-07 16:46:15 -04002120 static GrOp::Owner Make(GrRecordingContext* context,
2121 GrPaint&& paint,
2122 const SkMatrix& viewMatrix,
2123 const SkRect& ellipse,
2124 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002125 DeviceSpaceParams params;
2126 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2127 params.fXRadius = SkScalarHalf(ellipse.width());
2128 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002129
bsalomon4b4a7cc2016-07-08 04:42:54 -07002130 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002131 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2132 ? DIEllipseStyle::kStroke
2133 : (SkStrokeRec::kHairline_Style == style)
2134 ? DIEllipseStyle::kHairline
2135 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002136
Brian Salomon05441c42017-05-15 16:45:49 -04002137 params.fInnerXRadius = 0;
2138 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002139 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2140 SkScalar strokeWidth = stroke.getWidth();
2141
2142 if (SkScalarNearlyZero(strokeWidth)) {
2143 strokeWidth = SK_ScalarHalf;
2144 } else {
2145 strokeWidth *= SK_ScalarHalf;
2146 }
2147
2148 // we only handle thick strokes for near-circular ellipses
2149 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002150 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2151 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002152 return nullptr;
2153 }
2154
2155 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002156 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2157 (strokeWidth * strokeWidth) * params.fXRadius) {
2158 return nullptr;
2159 }
2160 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2161 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002162 return nullptr;
2163 }
2164
2165 // set inner radius (if needed)
2166 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002167 params.fInnerXRadius = params.fXRadius - strokeWidth;
2168 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002169 }
2170
Brian Salomon05441c42017-05-15 16:45:49 -04002171 params.fXRadius += strokeWidth;
2172 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002173 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002174
2175 // For large ovals with low precision floats, we fall back to the path renderer.
2176 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2177 // minimum value to avoid divides by zero. With large ovals and low precision this
2178 // leads to blurring at the edge of the oval.
2179 const SkScalar kMaxOvalRadius = 16384;
2180 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2181 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2182 return nullptr;
2183 }
2184
Brian Salomon05441c42017-05-15 16:45:49 -04002185 if (DIEllipseStyle::kStroke == params.fStyle &&
2186 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2187 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002188 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002189 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002190 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002191
Herb Derbyc76d4092020-10-07 16:46:15 -04002192 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002193 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002194 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002195 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002196 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002197 // This expands the outer rect so that after CTM we end up with a half-pixel border
2198 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2199 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2200 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2201 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2202 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2203 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002204
Brian Salomon05441c42017-05-15 16:45:49 -04002205 fEllipses.emplace_back(
2206 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2207 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2208 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2209 params.fCenter.fY - params.fYRadius - geoDy,
2210 params.fCenter.fX + params.fXRadius + geoDx,
2211 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002212 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002213 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002214 }
2215
Brian Salomon289e3d82016-12-14 15:52:56 -05002216 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002217
Chris Dalton1706cbf2019-05-21 19:35:29 -06002218 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002219 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002220 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002221 } else {
2222 fHelper.visitProxies(func);
2223 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002224 }
2225
Chris Dalton57ab06c2021-04-22 12:57:28 -06002226 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2227 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002228 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2229 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002230 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002231 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002232 GrProcessorAnalysisCoverage::kSingleChannel, color,
2233 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002234 }
2235
2236 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2237
bsalomone46f9fe2015-08-18 06:05:14 -07002238private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002239 GrProgramInfo* programInfo() override { return fProgramInfo; }
2240
Robert Phillips4133dc42020-03-11 15:55:55 -04002241 void onCreateProgramInfo(const GrCaps* caps,
2242 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002243 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002244 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002245 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002246 GrXferBarrierFlags renderPassXferBarriers,
2247 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002248 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2249 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002250 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002251
Brian Salomon8afde5f2020-04-01 16:22:00 -04002252 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002253 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002254 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002255 }
2256
Robert Phillips4490d922020-03-03 14:50:59 -05002257 void onPrepareDraws(Target* target) override {
2258 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002259 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002260 }
2261
Robert Phillips787fd9d2021-03-22 14:48:09 -04002262 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002263 GrVertexWriter verts{helper.vertices()};
2264 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002265 return;
2266 }
2267
Brian Salomon05441c42017-05-15 16:45:49 -04002268 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002269 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002270 SkScalar xRadius = ellipse.fXRadius;
2271 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002272
joshualitt76e7fb62015-02-11 08:52:27 -08002273 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002274 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2275 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002276
Brian Osman9d958b52018-11-13 12:46:56 -05002277 // By default, constructed so that inner offset is (0, 0) for all points
2278 SkScalar innerRatioX = -offsetDx;
2279 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002280
Brian Osman9d958b52018-11-13 12:46:56 -05002281 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002282 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002283 innerRatioX = xRadius / ellipse.fInnerXRadius;
2284 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002285 }
joshualitt76e7fb62015-02-11 08:52:27 -08002286
Brian Osman2b6e3902018-11-21 15:29:43 -05002287 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2288 color,
2289 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002290 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002291 origin_centered_tri_strip(innerRatioX + offsetDx,
2292 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002293 }
Robert Phillips4490d922020-03-03 14:50:59 -05002294 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002295 }
2296
2297 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002298 if (!fProgramInfo || !fMesh) {
2299 return;
2300 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002301
Chris Dalton765ed362020-03-16 17:34:44 -06002302 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002303 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002304 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002305 }
halcanary9d524f22016-03-29 09:03:52 -07002306
Herb Derbye25c3002020-10-27 15:57:27 -04002307 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002308 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002309 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002310 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002311 }
2312
bsalomoncdaa97b2016-03-08 08:30:14 -08002313 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002314 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002315 }
2316
joshualittd96a67b2015-05-05 14:09:05 -07002317 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002318 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002319 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002320 }
2321
Brian Salomon05441c42017-05-15 16:45:49 -04002322 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002323 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002324 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002325 }
2326
John Stilesaf366522020-08-13 09:57:34 -04002327#if GR_TEST_UTILS
2328 SkString onDumpInfo() const override {
2329 SkString string;
2330 for (const auto& geo : fEllipses) {
2331 string.appendf(
2332 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2333 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2334 "GeoDY: %.2f\n",
2335 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2336 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2337 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2338 }
2339 string += fHelper.dumpInfo();
2340 return string;
2341 }
2342#endif
2343
Brian Salomon05441c42017-05-15 16:45:49 -04002344 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2345 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002346
Brian Salomon05441c42017-05-15 16:45:49 -04002347 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002348 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002349 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002350 SkScalar fXRadius;
2351 SkScalar fYRadius;
2352 SkScalar fInnerXRadius;
2353 SkScalar fInnerYRadius;
2354 SkScalar fGeoDx;
2355 SkScalar fGeoDy;
2356 DIEllipseStyle fStyle;
2357 SkRect fBounds;
2358 };
2359
Brian Salomon05441c42017-05-15 16:45:49 -04002360 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002361 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002362 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002363 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002364
Chris Daltoneb694b72020-03-16 09:25:50 -06002365 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002366 GrProgramInfo* fProgramInfo = nullptr;
2367
John Stiles7571f9e2020-09-02 22:42:33 -04002368 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002369};
2370
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002371///////////////////////////////////////////////////////////////////////////////
2372
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002373// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002374//
2375// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2376// ____________
2377// |_|________|_|
2378// | | | |
2379// | | | |
2380// | | | |
2381// |_|________|_|
2382// |_|________|_|
2383//
2384// For strokes, we don't draw the center quad.
2385//
2386// For circular roundrects, in the case where the stroke width is greater than twice
2387// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002388// in the center. The shared vertices are duplicated so we can set a different outer radius
2389// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002390// ____________
2391// |_|________|_|
2392// | |\ ____ /| |
2393// | | | | | |
2394// | | |____| | |
2395// |_|/______\|_|
2396// |_|________|_|
2397//
2398// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002399//
2400// For filled rrects that need to provide a distance vector we resuse the overstroke
2401// geometry but make the inner rect degenerate (either a point or a horizontal or
2402// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002403
jvanverth84839f62016-08-29 10:16:40 -07002404static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002405 // clang-format off
2406 // overstroke quads
2407 // we place this at the beginning so that we can skip these indices when rendering normally
2408 16, 17, 19, 16, 19, 18,
2409 19, 17, 23, 19, 23, 21,
2410 21, 23, 22, 21, 22, 20,
2411 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002412
Brian Salomon289e3d82016-12-14 15:52:56 -05002413 // corners
2414 0, 1, 5, 0, 5, 4,
2415 2, 3, 7, 2, 7, 6,
2416 8, 9, 13, 8, 13, 12,
2417 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002418
Brian Salomon289e3d82016-12-14 15:52:56 -05002419 // edges
2420 1, 2, 6, 1, 6, 5,
2421 4, 5, 9, 4, 9, 8,
2422 6, 7, 11, 6, 11, 10,
2423 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002424
Brian Salomon289e3d82016-12-14 15:52:56 -05002425 // center
2426 // we place this at the end so that we can ignore these indices when not rendering as filled
2427 5, 6, 10, 5, 10, 9,
2428 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002429};
Brian Salomon289e3d82016-12-14 15:52:56 -05002430
jvanverth84839f62016-08-29 10:16:40 -07002431// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002432static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002433
jvanverth84839f62016-08-29 10:16:40 -07002434// overstroke count is arraysize minus the center indices
2435static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2436// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002437static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002438// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002439static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2440static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002441static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002442
jvanverthc3d0e422016-08-25 08:12:35 -07002443enum RRectType {
2444 kFill_RRectType,
2445 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002446 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002447};
2448
jvanverth84839f62016-08-29 10:16:40 -07002449static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002450 switch (type) {
2451 case kFill_RRectType:
2452 case kStroke_RRectType:
2453 return kVertsPerStandardRRect;
2454 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002455 return kVertsPerOverstrokeRRect;
2456 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002457 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002458}
2459
2460static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002461 switch (type) {
2462 case kFill_RRectType:
2463 return kIndicesPerFillRRect;
2464 case kStroke_RRectType:
2465 return kIndicesPerStrokeRRect;
2466 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002467 return kIndicesPerOverstrokeRRect;
2468 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002469 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002470}
2471
2472static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002473 switch (type) {
2474 case kFill_RRectType:
2475 case kStroke_RRectType:
2476 return gStandardRRectIndices;
2477 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002478 return gOverstrokeRRectIndices;
2479 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002480 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002481}
2482
joshualitt76e7fb62015-02-11 08:52:27 -08002483///////////////////////////////////////////////////////////////////////////////////////////////////
2484
Robert Phillips79839d42016-10-06 15:03:34 -04002485// For distance computations in the interior of filled rrects we:
2486//
2487// add a interior degenerate (point or line) rect
2488// each vertex of that rect gets -outerRad as its radius
2489// this makes the computation of the distance to the outer edge be negative
2490// negative values are caught and then handled differently in the GP's onEmitCode
2491// each vertex is also given the normalized x & y distance from the interior rect's edge
2492// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2493
Brian Salomon05441c42017-05-15 16:45:49 -04002494class CircularRRectOp : public GrMeshDrawOp {
2495private:
2496 using Helper = GrSimpleMeshDrawOpHelper;
2497
joshualitt76e7fb62015-02-11 08:52:27 -08002498public:
Brian Salomon25a88092016-12-01 09:36:50 -05002499 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002500
bsalomon4b4a7cc2016-07-08 04:42:54 -07002501 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2502 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002503 static GrOp::Owner Make(GrRecordingContext* context,
2504 GrPaint&& paint,
2505 const SkMatrix& viewMatrix,
2506 const SkRect& devRect,
2507 float devRadius,
2508 float devStrokeWidth,
2509 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002510 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002511 devRect, devRadius,
2512 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002513 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002514 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002515 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2516 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002517 : INHERITED(ClassID())
2518 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002519 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002520 SkRect bounds = devRect;
2521 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2522 SkScalar innerRadius = 0.0f;
2523 SkScalar outerRadius = devRadius;
2524 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002525 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002526 if (devStrokeWidth > 0) {
2527 if (SkScalarNearlyZero(devStrokeWidth)) {
2528 halfWidth = SK_ScalarHalf;
2529 } else {
2530 halfWidth = SkScalarHalf(devStrokeWidth);
2531 }
joshualitt76e7fb62015-02-11 08:52:27 -08002532
bsalomon4b4a7cc2016-07-08 04:42:54 -07002533 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002534 // Outset stroke by 1/4 pixel
2535 devStrokeWidth += 0.25f;
2536 // If stroke is greater than width or height, this is still a fill
2537 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002538 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002539 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002540 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002541 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002542 }
2543 outerRadius += halfWidth;
2544 bounds.outset(halfWidth, halfWidth);
2545 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002546
Greg Daniel2655ede2019-04-10 00:49:28 +00002547 // The radii are outset for two reasons. First, it allows the shader to simply perform
2548 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2549 // Second, the outer radius is used to compute the verts of the bounding box that is
2550 // rendered and the outset ensures the box will cover all partially covered by the rrect
2551 // corners.
2552 outerRadius += SK_ScalarHalf;
2553 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002554
Greg Daniel5faf4742019-10-01 15:14:44 -04002555 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002556
Greg Daniel2655ede2019-04-10 00:49:28 +00002557 // Expand the rect for aa to generate correct vertices.
2558 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002559
Brian Salomon05441c42017-05-15 16:45:49 -04002560 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002561 fVertCount = rrect_type_to_vert_count(type);
2562 fIndexCount = rrect_type_to_index_count(type);
2563 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002564 }
2565
Brian Salomon289e3d82016-12-14 15:52:56 -05002566 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002567
Chris Dalton1706cbf2019-05-21 19:35:29 -06002568 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002569 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002570 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002571 } else {
2572 fHelper.visitProxies(func);
2573 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002574 }
2575
Chris Dalton57ab06c2021-04-22 12:57:28 -06002576 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2577 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002578 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002579 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002580 GrProcessorAnalysisCoverage::kSingleChannel, color,
2581 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002582 }
2583
2584 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2585
Brian Salomon92aee3d2016-12-21 09:20:25 -05002586private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002587 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002588 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002589 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002590 SkASSERT(smInset < bigInset);
2591
2592 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002593 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2594 color,
2595 xOffset, 0.0f,
2596 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002597
2598 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002599 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2600 color,
2601 xOffset, 0.0f,
2602 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002603
Brian Osmana1d4eb92018-12-06 16:33:10 -05002604 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2605 color,
2606 0.0f, 0.0f,
2607 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002608
Brian Osmana1d4eb92018-12-06 16:33:10 -05002609 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2610 color,
2611 0.0f, 0.0f,
2612 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002613
Brian Osmana1d4eb92018-12-06 16:33:10 -05002614 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2615 color,
2616 0.0f, 0.0f,
2617 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002618
Brian Osmana1d4eb92018-12-06 16:33:10 -05002619 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2620 color,
2621 0.0f, 0.0f,
2622 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002623
2624 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002625 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2626 color,
2627 xOffset, 0.0f,
2628 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002629
2630 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002631 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2632 color,
2633 xOffset, 0.0f,
2634 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002635 }
2636
Robert Phillips2669a7b2020-03-12 12:07:19 -04002637 GrProgramInfo* programInfo() override { return fProgramInfo; }
2638
Robert Phillips4133dc42020-03-11 15:55:55 -04002639 void onCreateProgramInfo(const GrCaps* caps,
2640 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002641 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002642 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002643 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002644 GrXferBarrierFlags renderPassXferBarriers,
2645 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002646 // Invert the view matrix as a local matrix (if any other processors require coords).
2647 SkMatrix localMatrix;
2648 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002649 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002650 }
2651
Robert Phillips4490d922020-03-03 14:50:59 -05002652 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002653 false, false, false, false,
2654 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002655
Brian Salomon8afde5f2020-04-01 16:22:00 -04002656 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002657 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002658 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002659 }
2660
Robert Phillips4490d922020-03-03 14:50:59 -05002661 void onPrepareDraws(Target* target) override {
2662 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002663 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002664 if (!fProgramInfo) {
2665 return;
2666 }
2667 }
2668
Brian Salomon12d22642019-01-29 14:38:50 -05002669 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002670 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002671
Robert Phillips787fd9d2021-03-22 14:48:09 -04002672 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002673 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002674 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002675 SkDebugf("Could not allocate vertices\n");
2676 return;
2677 }
2678
Brian Salomon12d22642019-01-29 14:38:50 -05002679 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002680 int firstIndex = 0;
2681 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2682 if (!indices) {
2683 SkDebugf("Could not allocate indices\n");
2684 return;
2685 }
2686
2687 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002688 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002689 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002690 SkScalar outerRadius = rrect.fOuterRadius;
2691 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002692
Brian Salomon289e3d82016-12-14 15:52:56 -05002693 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2694 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002695
Brian Salomon289e3d82016-12-14 15:52:56 -05002696 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002697 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002698 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002699 SkScalar innerRadius = rrect.fType != kFill_RRectType
2700 ? rrect.fInnerRadius / rrect.fOuterRadius
2701 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002702 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002703 verts.write(bounds.fLeft, yCoords[i],
2704 color,
2705 -1.0f, yOuterRadii[i],
2706 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002707
Brian Osmana1d4eb92018-12-06 16:33:10 -05002708 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2709 color,
2710 0.0f, yOuterRadii[i],
2711 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002712
Brian Osmana1d4eb92018-12-06 16:33:10 -05002713 verts.write(bounds.fRight - outerRadius, yCoords[i],
2714 color,
2715 0.0f, yOuterRadii[i],
2716 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002717
Brian Osmana1d4eb92018-12-06 16:33:10 -05002718 verts.write(bounds.fRight, yCoords[i],
2719 color,
2720 1.0f, yOuterRadii[i],
2721 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002722 }
jvanverthc3d0e422016-08-25 08:12:35 -07002723 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002724 // Effectively this is an additional stroked rrect, with its
2725 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2726 // This will give us correct AA in the center and the correct
2727 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002728 //
jvanvertha4f1af82016-08-29 07:17:47 -07002729 // Also, the outer offset is a constant vector pointing to the right, which
2730 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002731 if (kOverstroke_RRectType == rrect.fType) {
2732 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002733
Brian Salomon05441c42017-05-15 16:45:49 -04002734 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002735 // this is the normalized distance from the outer rectangle of this
2736 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002737 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002738
Brian Osmana1d4eb92018-12-06 16:33:10 -05002739 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002740 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002741 }
jvanverth6a397612016-08-26 08:15:33 -07002742
Brian Salomon05441c42017-05-15 16:45:49 -04002743 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2744 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002745 for (int i = 0; i < primIndexCount; ++i) {
2746 *indices++ = primIndices[i] + currStartVertex;
2747 }
2748
Brian Salomon05441c42017-05-15 16:45:49 -04002749 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002750 }
2751
Robert Phillips4490d922020-03-03 14:50:59 -05002752 fMesh = target->allocMesh();
2753 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002754 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002755 }
2756
2757 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002758 if (!fProgramInfo || !fMesh) {
2759 return;
2760 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002761
Chris Dalton765ed362020-03-16 17:34:44 -06002762 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002763 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002764 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002765 }
2766
Herb Derbye25c3002020-10-27 15:57:27 -04002767 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002768 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002769
2770 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002771 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002772 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002773 }
2774
Brian Salomon05441c42017-05-15 16:45:49 -04002775 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002776 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002777 }
2778
Brian Salomon05441c42017-05-15 16:45:49 -04002779 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002780 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2781 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002782 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002783 }
2784
Brian Salomon05441c42017-05-15 16:45:49 -04002785 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002786 fVertCount += that->fVertCount;
2787 fIndexCount += that->fIndexCount;
2788 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002789 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002790 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002791 }
2792
John Stilesaf366522020-08-13 09:57:34 -04002793#if GR_TEST_UTILS
2794 SkString onDumpInfo() const override {
2795 SkString string;
2796 for (int i = 0; i < fRRects.count(); ++i) {
2797 string.appendf(
2798 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2799 "InnerRad: %.2f, OuterRad: %.2f\n",
2800 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2801 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2802 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2803 fRRects[i].fOuterRadius);
2804 }
2805 string += fHelper.dumpInfo();
2806 return string;
2807 }
2808#endif
2809
Brian Salomon05441c42017-05-15 16:45:49 -04002810 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002811 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002812 SkScalar fInnerRadius;
2813 SkScalar fOuterRadius;
2814 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002815 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002816 };
2817
Brian Salomon289e3d82016-12-14 15:52:56 -05002818 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002819 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002820 int fVertCount;
2821 int fIndexCount;
2822 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002823 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002824 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002825
Chris Daltoneb694b72020-03-16 09:25:50 -06002826 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002827 GrProgramInfo* fProgramInfo = nullptr;
2828
John Stiles7571f9e2020-09-02 22:42:33 -04002829 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002830};
2831
jvanverth84839f62016-08-29 10:16:40 -07002832static const int kNumRRectsInIndexBuffer = 256;
2833
2834GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2835GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002836static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2837 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002838 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2839 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2840 switch (type) {
2841 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002842 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002843 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2844 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002845 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002846 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002847 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2848 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002849 default:
2850 SkASSERT(false);
2851 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002852 }
jvanverth84839f62016-08-29 10:16:40 -07002853}
2854
Brian Salomon05441c42017-05-15 16:45:49 -04002855class EllipticalRRectOp : public GrMeshDrawOp {
2856private:
2857 using Helper = GrSimpleMeshDrawOpHelper;
2858
joshualitt76e7fb62015-02-11 08:52:27 -08002859public:
Brian Salomon25a88092016-12-01 09:36:50 -05002860 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002861
bsalomon4b4a7cc2016-07-08 04:42:54 -07002862 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2863 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002864 static GrOp::Owner Make(GrRecordingContext* context,
2865 GrPaint&& paint,
2866 const SkMatrix& viewMatrix,
2867 const SkRect& devRect,
2868 float devXRadius,
2869 float devYRadius,
2870 SkVector devStrokeWidths,
2871 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002872 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2873 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002874 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2875 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002876 if (devStrokeWidths.fX > 0) {
2877 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2878 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2879 } else {
2880 devStrokeWidths.scale(SK_ScalarHalf);
2881 }
joshualitt76e7fb62015-02-11 08:52:27 -08002882
bsalomon4b4a7cc2016-07-08 04:42:54 -07002883 // we only handle thick strokes for near-circular ellipses
2884 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002885 (SK_ScalarHalf * devXRadius > devYRadius ||
2886 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002887 return nullptr;
2888 }
2889
2890 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002891 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2892 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 return nullptr;
2894 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002895 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2896 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002897 return nullptr;
2898 }
Brian Salomon05441c42017-05-15 16:45:49 -04002899 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002900 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002901 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002902 devXRadius, devYRadius, devStrokeWidths,
2903 strokeOnly);
2904 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002905
Herb Derbyc76d4092020-10-07 16:46:15 -04002906 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002907 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2908 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002909 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002910 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002911 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002912 SkScalar innerXRadius = 0.0f;
2913 SkScalar innerYRadius = 0.0f;
2914 SkRect bounds = devRect;
2915 bool stroked = false;
2916 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002917 // this is legit only if scale & translation (which should be the case at the moment)
2918 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002919 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2920 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002921 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2922 }
2923
Brian Salomon05441c42017-05-15 16:45:49 -04002924 devXRadius += devStrokeHalfWidths.fX;
2925 devYRadius += devStrokeHalfWidths.fY;
2926 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002927 }
2928
Brian Salomon05441c42017-05-15 16:45:49 -04002929 fStroked = stroked;
2930 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002931 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002932 // Expand the rect for aa in order to generate the correct vertices.
2933 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002934 fRRects.emplace_back(
2935 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002936 }
2937
Brian Salomon289e3d82016-12-14 15:52:56 -05002938 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002939
Chris Dalton1706cbf2019-05-21 19:35:29 -06002940 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002941 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002942 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002943 } else {
2944 fHelper.visitProxies(func);
2945 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002946 }
2947
Chris Dalton57ab06c2021-04-22 12:57:28 -06002948 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2949 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002950 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002951 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002952 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002953 GrProcessorAnalysisCoverage::kSingleChannel, color,
2954 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002955 }
2956
2957 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2958
bsalomone46f9fe2015-08-18 06:05:14 -07002959private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002960 GrProgramInfo* programInfo() override { return fProgramInfo; }
2961
Robert Phillips4133dc42020-03-11 15:55:55 -04002962 void onCreateProgramInfo(const GrCaps* caps,
2963 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002964 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002965 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002966 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002967 GrXferBarrierFlags renderPassXferBarriers,
2968 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002969 SkMatrix localMatrix;
2970 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002972 }
2973
Robert Phillips4490d922020-03-03 14:50:59 -05002974 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2975 fUseScale, localMatrix);
2976
Brian Salomon8afde5f2020-04-01 16:22:00 -04002977 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002978 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002979 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002980 }
2981
Robert Phillips4490d922020-03-03 14:50:59 -05002982 void onPrepareDraws(Target* target) override {
2983 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002984 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002985 if (!fProgramInfo) {
2986 return;
2987 }
2988 }
joshualitt76e7fb62015-02-11 08:52:27 -08002989
bsalomonb5238a72015-05-05 07:49:49 -07002990 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002991 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002992 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2993 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002994
Brian Salomon12d22642019-01-29 14:38:50 -05002995 if (!indexBuffer) {
2996 SkDebugf("Could not allocate indices\n");
2997 return;
2998 }
Robert Phillips4490d922020-03-03 14:50:59 -05002999 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003000 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003001 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003002 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003003 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003004 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003005 SkDebugf("Could not allocate vertices\n");
3006 return;
3007 }
3008
Brian Salomon05441c42017-05-15 16:45:49 -04003009 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003010 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003011 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003012 float reciprocalRadii[4] = {
3013 SkScalarInvert(rrect.fXRadius),
3014 SkScalarInvert(rrect.fYRadius),
3015 SkScalarInvert(rrect.fInnerXRadius),
3016 SkScalarInvert(rrect.fInnerYRadius)
3017 };
joshualitt76e7fb62015-02-11 08:52:27 -08003018
Brian Osmane3afdd52020-10-28 10:49:56 -04003019 // If the stroke width is exactly double the radius, the inner radii will be zero.
3020 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3021 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3022 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3023
joshualitt76e7fb62015-02-11 08:52:27 -08003024 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003025 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3026 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003027
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003028 SkScalar xMaxOffset = xOuterRadius;
3029 SkScalar yMaxOffset = yOuterRadius;
3030 if (!fStroked) {
3031 // For filled rrects we map a unit circle in the vertex attributes rather than
3032 // computing an ellipse and modifying that distance, so we normalize to 1.
3033 xMaxOffset /= rrect.fXRadius;
3034 yMaxOffset /= rrect.fYRadius;
3035 }
3036
Brian Salomon05441c42017-05-15 16:45:49 -04003037 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003038
Brian Salomon289e3d82016-12-14 15:52:56 -05003039 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3040 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003041 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003042 SK_ScalarNearlyZero, // we're using inversesqrt() in
3043 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003044 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003045
Brian Osman788b9162020-02-07 10:36:46 -05003046 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003047 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003048 verts.write(bounds.fLeft, yCoords[i],
3049 color,
3050 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003051 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003052 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003053
Brian Osmana1d4eb92018-12-06 16:33:10 -05003054 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3055 color,
3056 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003057 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003059
Brian Osmana1d4eb92018-12-06 16:33:10 -05003060 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3061 color,
3062 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003063 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003064 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003065
Brian Osmana1d4eb92018-12-06 16:33:10 -05003066 verts.write(bounds.fRight, yCoords[i],
3067 color,
3068 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003069 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003070 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003071 }
3072 }
Robert Phillips4490d922020-03-03 14:50:59 -05003073 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003074 }
3075
3076 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003077 if (!fProgramInfo || !fMesh) {
3078 return;
3079 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003080
Chris Dalton765ed362020-03-16 17:34:44 -06003081 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003082 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003083 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003084 }
3085
Herb Derbye25c3002020-10-27 15:57:27 -04003086 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003087 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003088
Brian Salomon05441c42017-05-15 16:45:49 -04003089 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003090 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003091 }
3092
bsalomoncdaa97b2016-03-08 08:30:14 -08003093 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003094 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003095 }
3096
Brian Salomon05441c42017-05-15 16:45:49 -04003097 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003098 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3099 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003100 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003101 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003102
Brian Salomon05441c42017-05-15 16:45:49 -04003103 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003104 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003105 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003106 }
3107
John Stilesaf366522020-08-13 09:57:34 -04003108#if GR_TEST_UTILS
3109 SkString onDumpInfo() const override {
3110 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3111 for (const auto& geo : fRRects) {
3112 string.appendf(
3113 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3114 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3115 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3116 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3117 geo.fInnerXRadius, geo.fInnerYRadius);
3118 }
3119 string += fHelper.dumpInfo();
3120 return string;
3121 }
3122#endif
3123
Brian Salomon05441c42017-05-15 16:45:49 -04003124 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003125 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003126 SkScalar fXRadius;
3127 SkScalar fYRadius;
3128 SkScalar fInnerXRadius;
3129 SkScalar fInnerYRadius;
3130 SkRect fDevBounds;
3131 };
3132
Brian Salomon289e3d82016-12-14 15:52:56 -05003133 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003134 Helper fHelper;
3135 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003136 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003137 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003138 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003139
Chris Daltoneb694b72020-03-16 09:25:50 -06003140 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003141 GrProgramInfo* fProgramInfo = nullptr;
3142
John Stiles7571f9e2020-09-02 22:42:33 -04003143 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003144};
3145
Herb Derbyc76d4092020-10-07 16:46:15 -04003146GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3147 GrPaint&& paint,
3148 const SkMatrix& viewMatrix,
3149 const SkRRect& rrect,
3150 const SkStrokeRec& stroke,
3151 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003152 SkASSERT(viewMatrix.rectStaysRect());
3153 SkASSERT(viewMatrix.isSimilarity());
3154 SkASSERT(rrect.isSimple());
3155 SkASSERT(!rrect.isOval());
3156 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3157
3158 // RRect ops only handle simple, but not too simple, rrects.
3159 // Do any matrix crunching before we reset the draw state for device coords.
3160 const SkRect& rrectBounds = rrect.getBounds();
3161 SkRect bounds;
3162 viewMatrix.mapRect(&bounds, rrectBounds);
3163
3164 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3165 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3166 viewMatrix[SkMatrix::kMSkewY]));
3167
3168 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3169 SkScalar scaledStroke = -1;
3170 SkScalar strokeWidth = stroke.getWidth();
3171 SkStrokeRec::Style style = stroke.getStyle();
3172
3173 bool isStrokeOnly =
3174 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3175 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3176
3177 if (hasStroke) {
3178 if (SkStrokeRec::kHairline_Style == style) {
3179 scaledStroke = SK_Scalar1;
3180 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003181 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3182 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003183 }
3184 }
3185
3186 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3187 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3188 // patch will have fractional coverage. This only matters when the interior is actually filled.
3189 // We could consider falling back to rect rendering here, since a tiny radius is
3190 // indistinguishable from a square corner.
3191 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3192 return nullptr;
3193 }
3194
3195 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3196 scaledStroke, isStrokeOnly);
3197}
3198
Herb Derbyc76d4092020-10-07 16:46:15 -04003199GrOp::Owner make_rrect_op(GrRecordingContext* context,
3200 GrPaint&& paint,
3201 const SkMatrix& viewMatrix,
3202 const SkRRect& rrect,
3203 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003204 SkASSERT(viewMatrix.rectStaysRect());
3205 SkASSERT(rrect.isSimple());
3206 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003207
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003208 // RRect ops only handle simple, but not too simple, rrects.
3209 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003210 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003211 SkRect bounds;
3212 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003213
Mike Reed242135a2018-02-22 13:41:39 -05003214 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003215 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3216 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3217 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3218 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003219
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003220 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003221
bsalomon4b4a7cc2016-07-08 04:42:54 -07003222 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3223 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003224 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003225
Brian Salomon289e3d82016-12-14 15:52:56 -05003226 bool isStrokeOnly =
3227 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003228 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3229
3230 if (hasStroke) {
3231 if (SkStrokeRec::kHairline_Style == style) {
3232 scaledStroke.set(1, 1);
3233 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003234 scaledStroke.fX = SkScalarAbs(
3235 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3236 scaledStroke.fY = SkScalarAbs(
3237 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003238 }
3239
Jim Van Verth64b85892019-06-17 12:01:46 -04003240 // if half of strokewidth is greater than radius, we don't handle that right now
3241 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3242 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003243 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003244 }
3245 }
3246
Brian Salomon8a97f562019-04-18 14:07:27 -04003247 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003248 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003249 std::swap(xRadius, yRadius);
3250 std::swap(scaledStroke.fX, scaledStroke.fY);
3251 }
3252
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003253 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3254 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3255 // patch will have fractional coverage. This only matters when the interior is actually filled.
3256 // We could consider falling back to rect rendering here, since a tiny radius is
3257 // indistinguishable from a square corner.
3258 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003259 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003260 }
3261
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003262 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003263 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3264 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003265}
3266
Herb Derbyc76d4092020-10-07 16:46:15 -04003267GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3268 GrPaint&& paint,
3269 const SkMatrix& viewMatrix,
3270 const SkRRect& rrect,
3271 const SkStrokeRec& stroke,
3272 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003273 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003274 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003275 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003276 }
3277
3278 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003279 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003280 }
3281
Greg Daniel2655ede2019-04-10 00:49:28 +00003282 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003283}
joshualitt3e708c52015-04-30 13:49:27 -07003284
bsalomon4b4a7cc2016-07-08 04:42:54 -07003285///////////////////////////////////////////////////////////////////////////////
3286
Herb Derbyc76d4092020-10-07 16:46:15 -04003287GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3288 GrPaint&& paint,
3289 const SkMatrix& viewMatrix,
3290 const SkRect& oval,
3291 const GrStyle& style,
3292 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003293 SkScalar width = oval.width();
3294 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3295 circle_stays_circle(viewMatrix));
3296
3297 auto r = width / 2.f;
3298 SkPoint center = { oval.centerX(), oval.centerY() };
3299 if (style.hasNonDashPathEffect()) {
3300 return nullptr;
3301 } else if (style.isDashed()) {
3302 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3303 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3304 return nullptr;
3305 }
3306 auto onInterval = style.dashIntervals()[0];
3307 auto offInterval = style.dashIntervals()[1];
3308 if (offInterval == 0) {
3309 GrStyle strokeStyle(style.strokeRec(), nullptr);
3310 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3311 strokeStyle, shaderCaps);
3312 } else if (onInterval == 0) {
3313 // There is nothing to draw but we have no way to indicate that here.
3314 return nullptr;
3315 }
3316 auto angularOnInterval = onInterval / r;
3317 auto angularOffInterval = offInterval / r;
3318 auto phaseAngle = style.dashPhase() / r;
3319 // Currently this function doesn't accept ovals with different start angles, though
3320 // it could.
3321 static const SkScalar kStartAngle = 0.f;
3322 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3323 style.strokeRec().getWidth(), kStartAngle,
3324 angularOnInterval, angularOffInterval, phaseAngle);
3325 }
3326 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3327}
3328
Herb Derbyc76d4092020-10-07 16:46:15 -04003329GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3330 GrPaint&& paint,
3331 const SkMatrix& viewMatrix,
3332 const SkRect& oval,
3333 const GrStyle& style,
3334 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003335 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003336 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003337 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3338 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003339 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003340 }
3341
3342 if (style.pathEffect()) {
3343 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003344 }
3345
Stan Ilieveb868aa2017-02-21 11:06:16 -05003346 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003347 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003348 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003349 }
3350
Stan Ilieveb868aa2017-02-21 11:06:16 -05003351 // Otherwise, if we have shader derivative support, render as device-independent
3352 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003353 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3354 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3355 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3356 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3357 // Check for near-degenerate matrix
3358 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003359 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003360 style.strokeRec());
3361 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003362 }
3363
bsalomon4b4a7cc2016-07-08 04:42:54 -07003364 return nullptr;
3365}
3366
3367///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003368
Herb Derbyc76d4092020-10-07 16:46:15 -04003369GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3370 GrPaint&& paint,
3371 const SkMatrix& viewMatrix,
3372 const SkRect& oval, SkScalar startAngle,
3373 SkScalar sweepAngle, bool useCenter,
3374 const GrStyle& style,
3375 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003376 SkASSERT(!oval.isEmpty());
3377 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003378 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003379 if (SkScalarAbs(sweepAngle) >= 360.f) {
3380 return nullptr;
3381 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003382 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3383 return nullptr;
3384 }
3385 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003386 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3387 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003388 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003389 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003390}
3391
3392///////////////////////////////////////////////////////////////////////////////
3393
Hal Canary6f6961e2017-01-31 13:50:44 -05003394#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003395
Brian Salomon05441c42017-05-15 16:45:49 -04003396GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003397 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003398 SkScalar rotate = random->nextSScalar1() * 360.f;
3399 SkScalar translateX = random->nextSScalar1() * 1000.f;
3400 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003401 SkScalar scale;
3402 do {
3403 scale = random->nextSScalar1() * 100.f;
3404 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003405 SkMatrix viewMatrix;
3406 viewMatrix.setRotate(rotate);
3407 viewMatrix.postTranslate(translateX, translateY);
3408 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003409 SkRect circle = GrTest::TestSquare(random);
3410 SkPoint center = {circle.centerX(), circle.centerY()};
3411 SkScalar radius = circle.width() / 2.f;
3412 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003413 CircleOp::ArcParams arcParamsTmp;
3414 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003415 if (random->nextBool()) {
3416 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003417 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3418 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003419 arcParams = &arcParamsTmp;
3420 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003421 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3422 center, radius,
3423 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003424 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003425 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003426 }
Mike Klein16885072018-12-11 09:54:31 -05003427 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003428 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003429}
3430
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003431GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3432 SkScalar rotate = random->nextSScalar1() * 360.f;
3433 SkScalar translateX = random->nextSScalar1() * 1000.f;
3434 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003435 SkScalar scale;
3436 do {
3437 scale = random->nextSScalar1() * 100.f;
3438 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003439 SkMatrix viewMatrix;
3440 viewMatrix.setRotate(rotate);
3441 viewMatrix.postTranslate(translateX, translateY);
3442 viewMatrix.postScale(scale, scale);
3443 SkRect circle = GrTest::TestSquare(random);
3444 SkPoint center = {circle.centerX(), circle.centerY()};
3445 SkScalar radius = circle.width() / 2.f;
3446 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3447 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3448 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3449 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3450 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003451 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3452 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003453 startAngle, onAngle, offAngle, phase);
3454}
3455
Brian Salomon05441c42017-05-15 16:45:49 -04003456GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003457 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003458 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003459 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003460 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003461}
3462
Brian Salomon05441c42017-05-15 16:45:49 -04003463GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003464 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003465 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003466 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003467 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003468}
3469
Jim Van Verth64b85892019-06-17 12:01:46 -04003470GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3471 do {
3472 SkScalar rotate = random->nextSScalar1() * 360.f;
3473 SkScalar translateX = random->nextSScalar1() * 1000.f;
3474 SkScalar translateY = random->nextSScalar1() * 1000.f;
3475 SkScalar scale;
3476 do {
3477 scale = random->nextSScalar1() * 100.f;
3478 } while (scale == 0);
3479 SkMatrix viewMatrix;
3480 viewMatrix.setRotate(rotate);
3481 viewMatrix.postTranslate(translateX, translateY);
3482 viewMatrix.postScale(scale, scale);
3483 SkRect rect = GrTest::TestRect(random);
3484 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3485 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3486 if (rrect.isOval()) {
3487 continue;
3488 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003489 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003490 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3491 GrTest::TestStrokeRec(random), nullptr);
3492 if (op) {
3493 return op;
3494 }
3495 assert_alive(paint);
3496 } while (true);
3497}
3498
Brian Salomon05441c42017-05-15 16:45:49 -04003499GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003500 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003501 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003502 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003503 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003504}
3505
3506#endif