blob: 9db38e56a0c55fe755d374df4f9a55a9267fd40c [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
Brian Salomon13b28732021-08-06 15:33:58 -040078 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050079 GLSLProcessor::GenKey(*this, caps, b);
80 }
81
Brian Salomonf95940b2021-08-09 15:56:24 -040082 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
83 return std::make_unique<GLSLProcessor>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050084 }
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
Brian Salomonf95940b2021-08-09 15:56:24 -0400114 class GLSLProcessor : public ProgramImpl {
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:
Brian Salomonf95940b2021-08-09 15:56:24 -0400235 using INHERITED = ProgramImpl;
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
Brian Salomon13b28732021-08-06 15:33:58 -0400287 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400288 GLSLProcessor::GenKey(*this, caps, b);
289 }
290
Brian Salomonf95940b2021-08-09 15:56:24 -0400291 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
292 return std::make_unique<GLSLProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400293 }
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 Salomonf95940b2021-08-09 15:56:24 -0400306 class GLSLProcessor : public ProgramImpl {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400307 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:
Brian Salomonf95940b2021-08-09 15:56:24 -0400504 using INHERITED = ProgramImpl;
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
Brian Salomon13b28732021-08-06 15:33:58 -0400552 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500553 GLSLProcessor::GenKey(*this, caps, b);
554 }
555
Brian Salomonf95940b2021-08-09 15:56:24 -0400556 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
557 return std::make_unique<GLSLProcessor>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500558 }
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
Brian Salomonf95940b2021-08-09 15:56:24 -0400578 class GLSLProcessor : public ProgramImpl {
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:
Brian Salomonf95940b2021-08-09 15:56:24 -0400700 using INHERITED = ProgramImpl;
Michael Ludwig553db622020-06-19 10:47:30 -0400701
702 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
703 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704 };
705
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500706 Attribute fInPosition;
707 Attribute fInColor;
708 Attribute fInEllipseOffset;
709 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400710
joshualitte3ababe2015-05-15 07:56:07 -0700711 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000712 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400713 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000714
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400715 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000716
John Stiles7571f9e2020-09-02 22:42:33 -0400717 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000718};
719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000721
Hal Canary6f6961e2017-01-31 13:50:44 -0500722#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500723GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400724 bool stroke = d->fRandom->nextBool();
725 bool wideColor = d->fRandom->nextBool();
726 bool useScale = d->fRandom->nextBool();
727 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
728 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000729}
Hal Canary6f6961e2017-01-31 13:50:44 -0500730#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000731
732///////////////////////////////////////////////////////////////////////////////
733
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000734/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000735 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000736 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
737 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
738 * using differentials.
739 *
740 * The result is device-independent and can be used with any affine matrix.
741 */
742
bsalomoncdaa97b2016-03-08 08:30:14 -0800743enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000744
bsalomoncdaa97b2016-03-08 08:30:14 -0800745class DIEllipseGeometryProcessor : public GrGeometryProcessor {
746public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500747 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
748 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600749 return arena->make([&](void* ptr) {
750 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
751 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500752 }
753
754 ~DIEllipseGeometryProcessor() override {}
755
756 const char* name() const override { return "DIEllipseGeometryProcessor"; }
757
Brian Salomon13b28732021-08-06 15:33:58 -0400758 void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500759 GLSLProcessor::GenKey(*this, caps, b);
760 }
761
Brian Salomonf95940b2021-08-09 15:56:24 -0400762 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
763 return std::make_unique<GLSLProcessor>();
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500764 }
765
766private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000767 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400768 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400769 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400770 , fViewMatrix(viewMatrix)
771 , fUseScale(useScale)
772 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500773 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500774 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400775 if (useScale) {
776 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
777 kFloat3_GrSLType};
778 } else {
779 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
780 kFloat2_GrSLType};
781 }
782 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000784 }
785
Brian Salomonf95940b2021-08-09 15:56:24 -0400786 class GLSLProcessor : public ProgramImpl {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000787 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500788 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000789
joshualitt465283c2015-09-11 08:19:35 -0700790 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400791 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800792 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800793 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800794 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000795
joshualittabb52a12015-01-13 15:02:10 -0800796 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800797 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800798
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
800 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800801 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500802 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700803
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400804 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800805 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500806 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800807
Chris Dalton60283612018-02-14 13:38:14 -0700808 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500809 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500810 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800811
joshualittabb52a12015-01-13 15:02:10 -0800812 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400813 WriteOutputPosition(vertBuilder,
814 uniformHandler,
815 *args.fShaderCaps,
816 gpArgs,
817 diegp.fInPosition.name(),
818 diegp.fViewMatrix,
819 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400820 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800821
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400823 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
824 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
825 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
826 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500827 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400828 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
829 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500830 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400831 if (diegp.fUseScale) {
832 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
833 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000834
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000836 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400837 if (args.fShaderCaps->floatIs32Bits()) {
838 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
839 } else {
840 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
841 }
842 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
843 if (diegp.fUseScale) {
844 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
845 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800846 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000848 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
849 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000851 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000852 }
853
854 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800855 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800856 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
857 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400858 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
859 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500860 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400861 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
862 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500863 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400864 if (diegp.fUseScale) {
865 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
866 }
867 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
868 if (!args.fShaderCaps->floatIs32Bits()) {
869 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
870 }
871 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
872 if (diegp.fUseScale) {
873 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
874 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000875 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000876 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000877
John Stiles4d7ac492021-03-09 20:16:43 -0500878 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879 }
880
robertphillips46d36f02015-01-18 08:14:14 -0800881 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400882 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700883 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800884 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500885 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
Brian Salomon5a328282021-04-14 10:32:25 -0400886 b->addBits(kMatrixKeyBits,
887 ComputeMatrixKey(shaderCaps, diegp.fViewMatrix),
888 "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889 }
890
Brian Osman609f1592020-07-01 15:14:39 -0400891 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400892 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400893 const GrGeometryProcessor& geomProc) override {
894 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700895
Brian Salomon5a328282021-04-14 10:32:25 -0400896 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897 }
898
899 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400900 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700901 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800902
Brian Salomonf95940b2021-08-09 15:56:24 -0400903 using INHERITED = ProgramImpl;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000904 };
905
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500906 Attribute fInPosition;
907 Attribute fInColor;
908 Attribute fInEllipseOffsets0;
909 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400910
Brian Salomon289e3d82016-12-14 15:52:56 -0500911 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400912 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500913 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000914
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400915 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000916
John Stiles7571f9e2020-09-02 22:42:33 -0400917 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000918};
919
bsalomoncdaa97b2016-03-08 08:30:14 -0800920GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000921
Hal Canary6f6961e2017-01-31 13:50:44 -0500922#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500923GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400924 bool wideColor = d->fRandom->nextBool();
925 bool useScale = d->fRandom->nextBool();
926 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
927 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
928 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000929}
Hal Canary6f6961e2017-01-31 13:50:44 -0500930#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000931
932///////////////////////////////////////////////////////////////////////////////
933
jvanverth6ca48822016-10-07 06:57:32 -0700934// We have two possible cases for geometry for a circle:
935
936// In the case of a normal fill, we draw geometry for the circle as an octagon.
937static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500938 // enter the octagon
939 // clang-format off
940 0, 1, 8, 1, 2, 8,
941 2, 3, 8, 3, 4, 8,
942 4, 5, 8, 5, 6, 8,
943 6, 7, 8, 7, 0, 8
944 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700945};
946
947// For stroked circles, we use two nested octagons.
948static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500949 // enter the octagon
950 // clang-format off
951 0, 1, 9, 0, 9, 8,
952 1, 2, 10, 1, 10, 9,
953 2, 3, 11, 2, 11, 10,
954 3, 4, 12, 3, 12, 11,
955 4, 5, 13, 4, 13, 12,
956 5, 6, 14, 5, 14, 13,
957 6, 7, 15, 6, 15, 14,
958 7, 0, 8, 7, 8, 15,
959 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700960};
961
Brian Osman9d958b52018-11-13 12:46:56 -0500962// Normalized geometry for octagons that circumscribe and lie on a circle:
963
964static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
965static constexpr SkPoint kOctagonOuter[] = {
966 SkPoint::Make(-kOctOffset, -1),
967 SkPoint::Make( kOctOffset, -1),
968 SkPoint::Make( 1, -kOctOffset),
969 SkPoint::Make( 1, kOctOffset),
970 SkPoint::Make( kOctOffset, 1),
971 SkPoint::Make(-kOctOffset, 1),
972 SkPoint::Make(-1, kOctOffset),
973 SkPoint::Make(-1, -kOctOffset),
974};
975
976// cosine and sine of pi/8
977static constexpr SkScalar kCosPi8 = 0.923579533f;
978static constexpr SkScalar kSinPi8 = 0.382683432f;
979static constexpr SkPoint kOctagonInner[] = {
980 SkPoint::Make(-kSinPi8, -kCosPi8),
981 SkPoint::Make( kSinPi8, -kCosPi8),
982 SkPoint::Make( kCosPi8, -kSinPi8),
983 SkPoint::Make( kCosPi8, kSinPi8),
984 SkPoint::Make( kSinPi8, kCosPi8),
985 SkPoint::Make(-kSinPi8, kCosPi8),
986 SkPoint::Make(-kCosPi8, kSinPi8),
987 SkPoint::Make(-kCosPi8, -kSinPi8),
988};
Brian Salomon289e3d82016-12-14 15:52:56 -0500989
jvanverth6ca48822016-10-07 06:57:32 -0700990static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
991static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
992static const int kVertsPerStrokeCircle = 16;
993static const int kVertsPerFillCircle = 9;
994
995static int circle_type_to_vert_count(bool stroked) {
996 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
997}
998
999static int circle_type_to_index_count(bool stroked) {
1000 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
1001}
1002
1003static const uint16_t* circle_type_to_indices(bool stroked) {
1004 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1005}
1006
1007///////////////////////////////////////////////////////////////////////////////
1008
Brian Salomon05441c42017-05-15 16:45:49 -04001009class CircleOp final : public GrMeshDrawOp {
1010private:
1011 using Helper = GrSimpleMeshDrawOpHelper;
1012
joshualitt76e7fb62015-02-11 08:52:27 -08001013public:
Brian Salomon25a88092016-12-01 09:36:50 -05001014 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001015
bsalomon4f3a0ca2016-08-22 13:14:26 -07001016 /** Optional extra params to render a partial arc rather than a full circle. */
1017 struct ArcParams {
1018 SkScalar fStartAngleRadians;
1019 SkScalar fSweepAngleRadians;
1020 bool fUseCenter;
1021 };
Brian Salomon05441c42017-05-15 16:45:49 -04001022
Herb Derbyc76d4092020-10-07 16:46:15 -04001023 static GrOp::Owner Make(GrRecordingContext* context,
1024 GrPaint&& paint,
1025 const SkMatrix& viewMatrix,
1026 SkPoint center,
1027 SkScalar radius,
1028 const GrStyle& style,
1029 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001030 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 if (style.hasPathEffect()) {
1032 return nullptr;
1033 }
Brian Salomon05441c42017-05-15 16:45:49 -04001034 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001035 SkStrokeRec::Style recStyle = stroke.getStyle();
1036 if (arcParams) {
1037 // Arc support depends on the style.
1038 switch (recStyle) {
1039 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001040 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041 return nullptr;
1042 case SkStrokeRec::kFill_Style:
1043 // This supports all fills.
1044 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001045 case SkStrokeRec::kStroke_Style:
1046 // Strokes that don't use the center point are supported with butt and round
1047 // caps.
1048 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1049 return nullptr;
1050 }
1051 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001052 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001053 // Hairline only supports butt cap. Round caps could be emulated by slightly
1054 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001055 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1056 return nullptr;
1057 }
1058 break;
1059 }
1060 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001061 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1062 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001063 }
1064
Herb Derbyc76d4092020-10-07 16:46:15 -04001065 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001066 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1067 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001068 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001069 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001070 const SkStrokeRec& stroke = style.strokeRec();
1071 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001072
Brian Salomon45c92202018-04-10 10:53:58 -04001073 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001074
bsalomon4b4a7cc2016-07-08 04:42:54 -07001075 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001077 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001078
Brian Salomon289e3d82016-12-14 15:52:56 -05001079 bool isStrokeOnly =
1080 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001081 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001082
jvanverth6ca48822016-10-07 06:57:32 -07001083 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001084 SkScalar outerRadius = radius;
1085 SkScalar halfWidth = 0;
1086 if (hasStroke) {
1087 if (SkScalarNearlyZero(strokeWidth)) {
1088 halfWidth = SK_ScalarHalf;
1089 } else {
1090 halfWidth = SkScalarHalf(strokeWidth);
1091 }
1092
1093 outerRadius += halfWidth;
1094 if (isStrokeOnly) {
1095 innerRadius = radius - halfWidth;
1096 }
1097 }
1098
1099 // The radii are outset for two reasons. First, it allows the shader to simply perform
1100 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1101 // Second, the outer radius is used to compute the verts of the bounding box that is
1102 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001103 outerRadius += SK_ScalarHalf;
1104 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001105 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001106 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001107
bsalomon4f3a0ca2016-08-22 13:14:26 -07001108 // This makes every point fully inside the intersection plane.
1109 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1110 // This makes every point fully outside the union plane.
1111 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001112 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001113 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1114 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001115 if (arcParams) {
1116 // The shader operates in a space where the circle is translated to be centered at the
1117 // origin. Here we compute points on the unit circle at the starting and ending angles.
1118 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001119 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1120 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001121 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001122 stopPoint.fY = SkScalarSin(endAngle);
1123 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001124
1125 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1126 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1127 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1128 startPoint.normalize();
1129 stopPoint.normalize();
1130
Brian Salomon3517aa72019-12-11 08:16:22 -05001131 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1132 // should orient the clip planes for arcs.
1133 SkASSERT(viewMatrix.isSimilarity());
1134 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1135 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1136 if (upperLeftDet < 0) {
1137 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001138 }
1139
Brian Salomon45c92202018-04-10 10:53:58 -04001140 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1141 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1142 SkPoint roundCaps[2];
1143 if (fRoundCaps) {
1144 // Compute the cap center points in the normalized space.
1145 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1146 roundCaps[0] = startPoint * midRadius;
1147 roundCaps[1] = stopPoint * midRadius;
1148 } else {
1149 roundCaps[0] = kUnusedRoundCaps[0];
1150 roundCaps[1] = kUnusedRoundCaps[1];
1151 }
1152
bsalomon4f3a0ca2016-08-22 13:14:26 -07001153 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001154 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1155 // center of the butts.
1156 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001157 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001158 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001159 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001160 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1161 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1162 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001163 if (useCenter) {
1164 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1165 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001166 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1167 if (arcParams->fSweepAngleRadians < 0) {
1168 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001169 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001170 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001171 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001172 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001173 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001174 color,
1175 innerRadius,
1176 outerRadius,
1177 {norm0.fX, norm0.fY, 0.5f},
1178 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1179 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001180 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001181 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001182 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001183 fClipPlaneIsect = false;
1184 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001185 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001186 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001187 color,
1188 innerRadius,
1189 outerRadius,
1190 {norm0.fX, norm0.fY, 0.5f},
1191 {norm1.fX, norm1.fY, 0.5f},
1192 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001193 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001194 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001195 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001196 fClipPlaneIsect = true;
1197 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001198 }
1199 } else {
1200 // We clip to a secant of the original circle.
1201 startPoint.scale(radius);
1202 stopPoint.scale(radius);
1203 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1204 norm.normalize();
1205 if (arcParams->fSweepAngleRadians > 0) {
1206 norm.negate();
1207 }
1208 SkScalar d = -norm.dot(startPoint) + 0.5f;
1209
Brian Salomon05441c42017-05-15 16:45:49 -04001210 fCircles.emplace_back(
1211 Circle{color,
1212 innerRadius,
1213 outerRadius,
1214 {norm.fX, norm.fY, d},
1215 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1216 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001217 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001218 devBounds,
1219 stroked});
1220 fClipPlane = true;
1221 fClipPlaneIsect = false;
1222 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001223 }
1224 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001225 fCircles.emplace_back(
1226 Circle{color,
1227 innerRadius,
1228 outerRadius,
1229 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1230 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1231 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001232 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001233 devBounds,
1234 stroked});
1235 fClipPlane = false;
1236 fClipPlaneIsect = false;
1237 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001238 }
bsalomon88cf17d2016-07-08 06:40:56 -07001239 // Use the original radius and stroke radius for the bounds so that it does not include the
1240 // AA bloat.
1241 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001242 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001243 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001244 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001245 fVertCount = circle_type_to_vert_count(stroked);
1246 fIndexCount = circle_type_to_index_count(stroked);
1247 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001248 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001249
Brian Salomon289e3d82016-12-14 15:52:56 -05001250 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001251
Robert Phillips294723d2021-06-17 09:23:58 -04001252 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001253 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001254 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001255 } else {
1256 fHelper.visitProxies(func);
1257 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001258 }
1259
Chris Dalton57ab06c2021-04-22 12:57:28 -06001260 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1261 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001262 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001263 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001264 GrProcessorAnalysisCoverage::kSingleChannel, color,
1265 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001266 }
1267
1268 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1269
bsalomone46f9fe2015-08-18 06:05:14 -07001270private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001271 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001272
Robert Phillips4133dc42020-03-11 15:55:55 -04001273 void onCreateProgramInfo(const GrCaps* caps,
1274 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001275 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001276 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001277 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001278 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001279 GrXferBarrierFlags renderPassXferBarriers,
1280 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001281 SkASSERT(!usesMSAASurface);
1282
bsalomoncdaa97b2016-03-08 08:30:14 -08001283 SkMatrix localMatrix;
1284 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001285 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001286 }
1287
Robert Phillips4490d922020-03-03 14:50:59 -05001288 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001289 fClipPlaneIsect, fClipPlaneUnion,
1290 fRoundCaps, fWideColor,
1291 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001292
Brian Salomon8afde5f2020-04-01 16:22:00 -04001293 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001294 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001295 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001296 }
1297
Robert Phillips71143952021-06-17 14:55:07 -04001298 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001299 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001300 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001301 if (!fProgramInfo) {
1302 return;
1303 }
1304 }
1305
Brian Salomon12d22642019-01-29 14:38:50 -05001306 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001307 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001308 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001309 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001310 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001311 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001312 return;
1313 }
1314
Brian Salomon12d22642019-01-29 14:38:50 -05001315 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001316 int firstIndex = 0;
1317 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1318 if (!indices) {
1319 SkDebugf("Could not allocate indices\n");
1320 return;
1321 }
1322
1323 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001324 for (const auto& circle : fCircles) {
1325 SkScalar innerRadius = circle.fInnerRadius;
1326 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001327 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001328 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001329
joshualitt76e7fb62015-02-11 08:52:27 -08001330 // The inner radius in the vertex data must be specified in normalized space.
1331 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001332 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001333
1334 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001335 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001336
Brian Osman9a24fee2018-08-03 09:48:42 -04001337 SkVector geoClipPlane = { 0, 0 };
1338 SkScalar offsetClipDist = SK_Scalar1;
1339 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1340 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1341 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1342 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1343 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1344 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1345 // the AA can extend just past the center of the circle.
1346 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1347 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1348 SkAssertResult(geoClipPlane.normalize());
1349 offsetClipDist = 0.5f / halfWidth;
1350 }
1351
Brian Osman7d8f82b2018-11-08 10:24:09 -05001352 for (int i = 0; i < 8; ++i) {
1353 // This clips the normalized offset to the half-plane we computed above. Then we
1354 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001355 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001356 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001357 vertices.write(center + offset * halfWidth,
1358 color,
1359 offset,
1360 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001361 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001362 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001363 }
1364 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001366 }
1367 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001369 }
1370 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001372 }
Brian Salomon45c92202018-04-10 10:53:58 -04001373 }
jvanverth6ca48822016-10-07 06:57:32 -07001374
Brian Salomon05441c42017-05-15 16:45:49 -04001375 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001376 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001377
Brian Osman7d8f82b2018-11-08 10:24:09 -05001378 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001379 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1380 color,
1381 kOctagonInner[i] * innerRadius,
1382 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001383 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001384 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001385 }
1386 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001387 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001388 }
1389 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001390 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001391 }
1392 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001393 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001394 }
Brian Salomon45c92202018-04-10 10:53:58 -04001395 }
jvanverth6ca48822016-10-07 06:57:32 -07001396 } else {
1397 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001398 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001399 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001400 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001401 }
jvanverth6ca48822016-10-07 06:57:32 -07001402 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001403 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001404 }
1405 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001406 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001407 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001408 if (fRoundCaps) {
1409 vertices.write(circle.fRoundCapCenters);
1410 }
jvanverth6ca48822016-10-07 06:57:32 -07001411 }
1412
Brian Salomon05441c42017-05-15 16:45:49 -04001413 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1414 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001415 for (int i = 0; i < primIndexCount; ++i) {
1416 *indices++ = primIndices[i] + currStartVertex;
1417 }
1418
Brian Salomon05441c42017-05-15 16:45:49 -04001419 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001420 }
jvanverth6ca48822016-10-07 06:57:32 -07001421
Robert Phillips4490d922020-03-03 14:50:59 -05001422 fMesh = target->allocMesh();
1423 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001424 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001425 }
1426
1427 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001428 if (!fProgramInfo || !fMesh) {
1429 return;
1430 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001431
Chris Dalton765ed362020-03-16 17:34:44 -06001432 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001433 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001434 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001435 }
1436
Herb Derbye25c3002020-10-27 15:57:27 -04001437 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001438 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001439
1440 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001441 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001442 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001443 }
1444
Brian Salomon05441c42017-05-15 16:45:49 -04001445 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001446 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001447 }
1448
Brian Salomon05441c42017-05-15 16:45:49 -04001449 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001450 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1451 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001452 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001453 }
1454
Brian Salomon289e3d82016-12-14 15:52:56 -05001455 // Because we've set up the ops that don't use the planes with noop values
1456 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001457 fClipPlane |= that->fClipPlane;
1458 fClipPlaneIsect |= that->fClipPlaneIsect;
1459 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001460 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001461 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001462
Brian Salomon05441c42017-05-15 16:45:49 -04001463 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001464 fVertCount += that->fVertCount;
1465 fIndexCount += that->fIndexCount;
1466 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001467 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001468 }
1469
John Stilesaf366522020-08-13 09:57:34 -04001470#if GR_TEST_UTILS
1471 SkString onDumpInfo() const override {
1472 SkString string;
1473 for (int i = 0; i < fCircles.count(); ++i) {
1474 string.appendf(
1475 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1476 "InnerRad: %.2f, OuterRad: %.2f\n",
1477 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1478 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1479 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1480 fCircles[i].fOuterRadius);
1481 }
1482 string += fHelper.dumpInfo();
1483 return string;
1484 }
1485#endif
1486
Brian Salomon05441c42017-05-15 16:45:49 -04001487 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001488 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001489 SkScalar fInnerRadius;
1490 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001491 SkScalar fClipPlane[3];
1492 SkScalar fIsectPlane[3];
1493 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001494 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001495 SkRect fDevBounds;
1496 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001497 };
1498
Brian Salomon289e3d82016-12-14 15:52:56 -05001499 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001500 Helper fHelper;
1501 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001502 int fVertCount;
1503 int fIndexCount;
1504 bool fAllFill;
1505 bool fClipPlane;
1506 bool fClipPlaneIsect;
1507 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001508 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001509 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001510
Chris Daltoneb694b72020-03-16 09:25:50 -06001511 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001512 GrProgramInfo* fProgramInfo = nullptr;
1513
John Stiles7571f9e2020-09-02 22:42:33 -04001514 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001515};
1516
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001517class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1518private:
1519 using Helper = GrSimpleMeshDrawOpHelper;
1520
1521public:
1522 DEFINE_OP_CLASS_ID
1523
Herb Derbyc76d4092020-10-07 16:46:15 -04001524 static GrOp::Owner Make(GrRecordingContext* context,
1525 GrPaint&& paint,
1526 const SkMatrix& viewMatrix,
1527 SkPoint center,
1528 SkScalar radius,
1529 SkScalar strokeWidth,
1530 SkScalar startAngle,
1531 SkScalar onAngle,
1532 SkScalar offAngle,
1533 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001534 SkASSERT(circle_stays_circle(viewMatrix));
1535 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001536 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1537 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001538 onAngle, offAngle, phaseAngle);
1539 }
1540
Herb Derbyc76d4092020-10-07 16:46:15 -04001541 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001542 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1543 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1544 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001545 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001546 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001547 SkASSERT(circle_stays_circle(viewMatrix));
1548 viewMatrix.mapPoints(&center, 1);
1549 radius = viewMatrix.mapRadius(radius);
1550 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1551
1552 // Determine the angle where the circle starts in device space and whether its orientation
1553 // has been reversed.
1554 SkVector start;
1555 bool reflection;
1556 if (!startAngle) {
1557 start = {1, 0};
1558 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001559 start.fY = SkScalarSin(startAngle);
1560 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001561 }
1562 viewMatrix.mapVectors(&start, 1);
1563 startAngle = SkScalarATan2(start.fY, start.fX);
1564 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1565 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1566
1567 auto totalAngle = onAngle + offAngle;
1568 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1569
1570 SkScalar halfWidth = 0;
1571 if (SkScalarNearlyZero(strokeWidth)) {
1572 halfWidth = SK_ScalarHalf;
1573 } else {
1574 halfWidth = SkScalarHalf(strokeWidth);
1575 }
1576
1577 SkScalar outerRadius = radius + halfWidth;
1578 SkScalar innerRadius = radius - halfWidth;
1579
1580 // The radii are outset for two reasons. First, it allows the shader to simply perform
1581 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1582 // Second, the outer radius is used to compute the verts of the bounding box that is
1583 // rendered and the outset ensures the box will cover all partially covered by the circle.
1584 outerRadius += SK_ScalarHalf;
1585 innerRadius -= SK_ScalarHalf;
1586 fViewMatrixIfUsingLocalCoords = viewMatrix;
1587
1588 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1589 center.fX + outerRadius, center.fY + outerRadius);
1590
1591 // We store whether there is a reflection as a negative total angle.
1592 if (reflection) {
1593 totalAngle = -totalAngle;
1594 }
1595 fCircles.push_back(Circle{
1596 color,
1597 outerRadius,
1598 innerRadius,
1599 onAngle,
1600 totalAngle,
1601 startAngle,
1602 phaseAngle,
1603 devBounds
1604 });
1605 // Use the original radius and stroke radius for the bounds so that it does not include the
1606 // AA bloat.
1607 radius += halfWidth;
1608 this->setBounds(
1609 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001610 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001611 fVertCount = circle_type_to_vert_count(true);
1612 fIndexCount = circle_type_to_index_count(true);
1613 }
1614
1615 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1616
Robert Phillips294723d2021-06-17 09:23:58 -04001617 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001618 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001619 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001620 } else {
1621 fHelper.visitProxies(func);
1622 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001623 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001624
Chris Dalton57ab06c2021-04-22 12:57:28 -06001625 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1626 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001627 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001628 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001629 GrProcessorAnalysisCoverage::kSingleChannel, color,
1630 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001631 }
1632
1633 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1634
1635private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001636 GrProgramInfo* programInfo() override { return fProgramInfo; }
1637
Robert Phillips4133dc42020-03-11 15:55:55 -04001638 void onCreateProgramInfo(const GrCaps* caps,
1639 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001640 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001641 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001642 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001643 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001644 GrXferBarrierFlags renderPassXferBarriers,
1645 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06001646 SkASSERT(!usesMSAASurface);
1647
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001648 SkMatrix localMatrix;
1649 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001650 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001651 }
1652
1653 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001654 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001655 fWideColor,
1656 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001657
Brian Salomon8afde5f2020-04-01 16:22:00 -04001658 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001659 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001660 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001661 }
1662
Robert Phillips71143952021-06-17 14:55:07 -04001663 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001664 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001665 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001666 if (!fProgramInfo) {
1667 return;
1668 }
1669 }
1670
Brian Salomon12d22642019-01-29 14:38:50 -05001671 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001672 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001673 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001674 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001675 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676 SkDebugf("Could not allocate vertices\n");
1677 return;
1678 }
1679
Brian Salomon12d22642019-01-29 14:38:50 -05001680 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001681 int firstIndex = 0;
1682 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1683 if (!indices) {
1684 SkDebugf("Could not allocate indices\n");
1685 return;
1686 }
1687
1688 int currStartVertex = 0;
1689 for (const auto& circle : fCircles) {
1690 // The inner radius in the vertex data must be specified in normalized space so that
1691 // length() can be called with smaller values to avoid precision issues with half
1692 // floats.
1693 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1694 const SkRect& bounds = circle.fDevBounds;
1695 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001696 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1697 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1698 };
1699 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001701 dashParams.totalAngle = -dashParams.totalAngle;
1702 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001703 }
1704
Brian Osmane3caf2d2018-11-21 13:48:36 -05001705 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001706
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001707 // The bounding geometry for the circle is composed of an outer bounding octagon and
1708 // an inner bounded octagon.
1709
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001710 // Compute the vertices of the outer octagon.
1711 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1712 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001713
1714 auto reflectY = [=](const SkPoint& p) {
1715 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001716 };
Brian Osman9d958b52018-11-13 12:46:56 -05001717
1718 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001719 vertices.write(center + kOctagonOuter[i] * halfWidth,
1720 color,
1721 reflectY(kOctagonOuter[i]),
1722 circle.fOuterRadius,
1723 normInnerRadius,
1724 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001725 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001726
1727 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001728 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001729 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1730 color,
1731 reflectY(kOctagonInner[i]) * normInnerRadius,
1732 circle.fOuterRadius,
1733 normInnerRadius,
1734 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001735 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001736
1737 const uint16_t* primIndices = circle_type_to_indices(true);
1738 const int primIndexCount = circle_type_to_index_count(true);
1739 for (int i = 0; i < primIndexCount; ++i) {
1740 *indices++ = primIndices[i] + currStartVertex;
1741 }
1742
1743 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001744 }
1745
Robert Phillips4490d922020-03-03 14:50:59 -05001746 fMesh = target->allocMesh();
1747 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001748 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001749 }
1750
1751 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001752 if (!fProgramInfo || !fMesh) {
1753 return;
1754 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001755
Chris Dalton765ed362020-03-16 17:34:44 -06001756 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001757 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001758 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001759 }
1760
Herb Derbye25c3002020-10-27 15:57:27 -04001761 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001762 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1763
1764 // can only represent 65535 unique vertices with 16-bit indices
1765 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001766 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001767 }
1768
1769 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001770 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001771 }
1772
1773 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001774 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1775 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001776 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001777 }
1778
1779 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001780 fVertCount += that->fVertCount;
1781 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001782 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001783 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784 }
1785
John Stilesaf366522020-08-13 09:57:34 -04001786#if GR_TEST_UTILS
1787 SkString onDumpInfo() const override {
1788 SkString string;
1789 for (int i = 0; i < fCircles.count(); ++i) {
1790 string.appendf(
1791 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1792 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1793 "Phase: %.2f\n",
1794 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1795 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1796 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1797 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1798 fCircles[i].fPhaseAngle);
1799 }
1800 string += fHelper.dumpInfo();
1801 return string;
1802 }
1803#endif
1804
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001805 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001806 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001807 SkScalar fOuterRadius;
1808 SkScalar fInnerRadius;
1809 SkScalar fOnAngle;
1810 SkScalar fTotalAngle;
1811 SkScalar fStartAngle;
1812 SkScalar fPhaseAngle;
1813 SkRect fDevBounds;
1814 };
1815
1816 SkMatrix fViewMatrixIfUsingLocalCoords;
1817 Helper fHelper;
1818 SkSTArray<1, Circle, true> fCircles;
1819 int fVertCount;
1820 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001821 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001822
Chris Daltoneb694b72020-03-16 09:25:50 -06001823 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001824 GrProgramInfo* fProgramInfo = nullptr;
1825
John Stiles7571f9e2020-09-02 22:42:33 -04001826 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001827};
1828
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001829///////////////////////////////////////////////////////////////////////////////
1830
Brian Salomon05441c42017-05-15 16:45:49 -04001831class EllipseOp : public GrMeshDrawOp {
1832private:
1833 using Helper = GrSimpleMeshDrawOpHelper;
1834
1835 struct DeviceSpaceParams {
1836 SkPoint fCenter;
1837 SkScalar fXRadius;
1838 SkScalar fYRadius;
1839 SkScalar fInnerXRadius;
1840 SkScalar fInnerYRadius;
1841 };
1842
joshualitt76e7fb62015-02-11 08:52:27 -08001843public:
Brian Salomon25a88092016-12-01 09:36:50 -05001844 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001845
Herb Derbyc76d4092020-10-07 16:46:15 -04001846 static GrOp::Owner Make(GrRecordingContext* context,
1847 GrPaint&& paint,
1848 const SkMatrix& viewMatrix,
1849 const SkRect& ellipse,
1850 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001851 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001853 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1854 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001855 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1856 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001857 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1858 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1859 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1860 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001861
bsalomon4b4a7cc2016-07-08 04:42:54 -07001862 // do (potentially) anisotropic mapping of stroke
1863 SkVector scaledStroke;
1864 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001865 scaledStroke.fX = SkScalarAbs(
1866 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1867 scaledStroke.fY = SkScalarAbs(
1868 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869
1870 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001871 bool isStrokeOnly =
1872 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1874
Brian Salomon05441c42017-05-15 16:45:49 -04001875 params.fInnerXRadius = 0;
1876 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001877 if (hasStroke) {
1878 if (SkScalarNearlyZero(scaledStroke.length())) {
1879 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1880 } else {
1881 scaledStroke.scale(SK_ScalarHalf);
1882 }
1883
1884 // we only handle thick strokes for near-circular ellipses
1885 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001886 (0.5f * params.fXRadius > params.fYRadius ||
1887 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001888 return nullptr;
1889 }
1890
1891 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001892 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1893 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1894 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1895 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001896 return nullptr;
1897 }
1898
1899 // this is legit only if scale & translation (which should be the case at the moment)
1900 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001901 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1902 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001903 }
1904
Brian Salomon05441c42017-05-15 16:45:49 -04001905 params.fXRadius += scaledStroke.fX;
1906 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001907 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001908
1909 // For large ovals with low precision floats, we fall back to the path renderer.
1910 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1911 // minimum value to avoid divides by zero. With large ovals and low precision this
1912 // leads to blurring at the edge of the oval.
1913 const SkScalar kMaxOvalRadius = 16384;
1914 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1915 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1916 return nullptr;
1917 }
1918
Greg Daniel2655ede2019-04-10 00:49:28 +00001919 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001920 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001921 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001922
Herb Derbyc76d4092020-10-07 16:46:15 -04001923 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001924 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001925 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001926 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001927 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001928 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001929 SkStrokeRec::Style style = stroke.getStyle();
1930 bool isStrokeOnly =
1931 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001932
Brian Salomon05441c42017-05-15 16:45:49 -04001933 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1934 params.fInnerXRadius, params.fInnerYRadius,
1935 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1936 params.fCenter.fY - params.fYRadius,
1937 params.fCenter.fX + params.fXRadius,
1938 params.fCenter.fY + params.fYRadius)});
1939
Greg Daniel5faf4742019-10-01 15:14:44 -04001940 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001941
Brian Salomon05441c42017-05-15 16:45:49 -04001942 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1943 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001944 }
joshualitt76e7fb62015-02-11 08:52:27 -08001945
Brian Salomon289e3d82016-12-14 15:52:56 -05001946 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001947
Robert Phillips294723d2021-06-17 09:23:58 -04001948 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001949 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001950 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001951 } else {
1952 fHelper.visitProxies(func);
1953 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001954 }
1955
Chris Dalton57ab06c2021-04-22 12:57:28 -06001956 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1957 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001958 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1959 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001960 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001961 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001962 GrProcessorAnalysisCoverage::kSingleChannel, color,
1963 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001964 }
1965
1966 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1967
bsalomone46f9fe2015-08-18 06:05:14 -07001968private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001969 GrProgramInfo* programInfo() override { return fProgramInfo; }
1970
Robert Phillips4133dc42020-03-11 15:55:55 -04001971 void onCreateProgramInfo(const GrCaps* caps,
1972 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001973 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001974 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001975 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001976 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001977 GrXferBarrierFlags renderPassXferBarriers,
1978 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001979 SkMatrix localMatrix;
1980 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001981 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001982 }
1983
Robert Phillips4490d922020-03-03 14:50:59 -05001984 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1985 fUseScale, localMatrix);
1986
Chris Dalton25da4062021-07-13 14:06:28 -06001987 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
1988 if (usesMSAASurface) {
1989 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
1990 }
1991
1992 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
1993 std::move(appliedClip),
1994 dstProxyView, gp,
1995 fHelper.detachProcessorSet(),
1996 GrPrimitiveType::kTriangles,
1997 renderPassXferBarriers,
1998 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05001999 }
2000
Robert Phillips71143952021-06-17 14:55:07 -04002001 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002002 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002003 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002004 if (!fProgramInfo) {
2005 return;
2006 }
2007 }
2008
Robert Phillips787fd9d2021-03-22 14:48:09 -04002009 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002010 GrVertexWriter verts{helper.vertices()};
2011 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05002012 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08002013 return;
2014 }
2015
Chris Dalton25da4062021-07-13 14:06:28 -06002016 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2017 // full sample coverage.
2018 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2019
Brian Salomon05441c42017-05-15 16:45:49 -04002020 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002021 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002022 SkScalar xRadius = ellipse.fXRadius;
2023 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002024
2025 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002026 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2027 SkScalarInvert(xRadius),
2028 SkScalarInvert(yRadius),
2029 SkScalarInvert(ellipse.fInnerXRadius),
2030 SkScalarInvert(ellipse.fInnerYRadius)
2031 };
Chris Dalton25da4062021-07-13 14:06:28 -06002032 SkScalar xMaxOffset = xRadius + aaBloat;
2033 SkScalar yMaxOffset = yRadius + aaBloat;
vjiaoblack977996d2016-06-30 12:20:54 -07002034
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002035 if (!fStroked) {
2036 // For filled ellipses we map a unit circle in the vertex attributes rather than
2037 // computing an ellipse and modifying that distance, so we normalize to 1
2038 xMaxOffset /= xRadius;
2039 yMaxOffset /= yRadius;
2040 }
2041
joshualitt76e7fb62015-02-11 08:52:27 -08002042 // The inner radius in the vertex data must be specified in normalized space.
Chris Dalton25da4062021-07-13 14:06:28 -06002043 verts.writeQuad(GrVertexWriter::TriStripFromRect(
2044 ellipse.fDevBounds.makeOutset(aaBloat, aaBloat)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002045 color,
2046 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002047 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002048 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002049 }
Robert Phillips4490d922020-03-03 14:50:59 -05002050 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002051 }
2052
2053 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002054 if (!fProgramInfo || !fMesh) {
2055 return;
2056 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002057
Chris Dalton765ed362020-03-16 17:34:44 -06002058 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002059 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002060 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002061 }
2062
Herb Derbye25c3002020-10-27 15:57:27 -04002063 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002064 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002065
Brian Salomon05441c42017-05-15 16:45:49 -04002066 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002067 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002068 }
2069
bsalomoncdaa97b2016-03-08 08:30:14 -08002070 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002071 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002072 }
2073
Brian Salomon05441c42017-05-15 16:45:49 -04002074 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002075 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2076 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002077 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002078 }
2079
Brian Salomon05441c42017-05-15 16:45:49 -04002080 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002081 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002082 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002083 }
2084
John Stilesaf366522020-08-13 09:57:34 -04002085#if GR_TEST_UTILS
2086 SkString onDumpInfo() const override {
2087 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2088 for (const auto& geo : fEllipses) {
2089 string.appendf(
2090 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2091 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2092 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2093 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2094 geo.fInnerXRadius, geo.fInnerYRadius);
2095 }
2096 string += fHelper.dumpInfo();
2097 return string;
2098 }
2099#endif
2100
Brian Salomon05441c42017-05-15 16:45:49 -04002101 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002102 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002103 SkScalar fXRadius;
2104 SkScalar fYRadius;
2105 SkScalar fInnerXRadius;
2106 SkScalar fInnerYRadius;
2107 SkRect fDevBounds;
2108 };
joshualitt76e7fb62015-02-11 08:52:27 -08002109
Brian Salomon289e3d82016-12-14 15:52:56 -05002110 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002111 Helper fHelper;
2112 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002113 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002114 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002115 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002116
Chris Daltoneb694b72020-03-16 09:25:50 -06002117 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002118 GrProgramInfo* fProgramInfo = nullptr;
2119
John Stiles7571f9e2020-09-02 22:42:33 -04002120 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002121};
2122
joshualitt76e7fb62015-02-11 08:52:27 -08002123/////////////////////////////////////////////////////////////////////////////////////////////////
2124
Brian Salomon05441c42017-05-15 16:45:49 -04002125class DIEllipseOp : public GrMeshDrawOp {
2126private:
2127 using Helper = GrSimpleMeshDrawOpHelper;
2128
2129 struct DeviceSpaceParams {
2130 SkPoint fCenter;
2131 SkScalar fXRadius;
2132 SkScalar fYRadius;
2133 SkScalar fInnerXRadius;
2134 SkScalar fInnerYRadius;
2135 DIEllipseStyle fStyle;
2136 };
2137
joshualitt76e7fb62015-02-11 08:52:27 -08002138public:
Brian Salomon25a88092016-12-01 09:36:50 -05002139 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002140
Herb Derbyc76d4092020-10-07 16:46:15 -04002141 static GrOp::Owner Make(GrRecordingContext* context,
2142 GrPaint&& paint,
2143 const SkMatrix& viewMatrix,
2144 const SkRect& ellipse,
2145 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002146 DeviceSpaceParams params;
2147 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2148 params.fXRadius = SkScalarHalf(ellipse.width());
2149 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002150
bsalomon4b4a7cc2016-07-08 04:42:54 -07002151 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002152 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2153 ? DIEllipseStyle::kStroke
2154 : (SkStrokeRec::kHairline_Style == style)
2155 ? DIEllipseStyle::kHairline
2156 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002157
Brian Salomon05441c42017-05-15 16:45:49 -04002158 params.fInnerXRadius = 0;
2159 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002160 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2161 SkScalar strokeWidth = stroke.getWidth();
2162
2163 if (SkScalarNearlyZero(strokeWidth)) {
2164 strokeWidth = SK_ScalarHalf;
2165 } else {
2166 strokeWidth *= SK_ScalarHalf;
2167 }
2168
2169 // we only handle thick strokes for near-circular ellipses
2170 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002171 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2172 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002173 return nullptr;
2174 }
2175
2176 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002177 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2178 (strokeWidth * strokeWidth) * params.fXRadius) {
2179 return nullptr;
2180 }
2181 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2182 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002183 return nullptr;
2184 }
2185
2186 // set inner radius (if needed)
2187 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002188 params.fInnerXRadius = params.fXRadius - strokeWidth;
2189 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002190 }
2191
Brian Salomon05441c42017-05-15 16:45:49 -04002192 params.fXRadius += strokeWidth;
2193 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002194 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002195
2196 // For large ovals with low precision floats, we fall back to the path renderer.
2197 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2198 // minimum value to avoid divides by zero. With large ovals and low precision this
2199 // leads to blurring at the edge of the oval.
2200 const SkScalar kMaxOvalRadius = 16384;
2201 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2202 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2203 return nullptr;
2204 }
2205
Brian Salomon05441c42017-05-15 16:45:49 -04002206 if (DIEllipseStyle::kStroke == params.fStyle &&
2207 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2208 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002209 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002210 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002211 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002212
Herb Derbyc76d4092020-10-07 16:46:15 -04002213 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002214 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002215 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002216 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002217 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002218 // This expands the outer rect so that after CTM we end up with a half-pixel border
2219 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2220 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2221 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2222 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Chris Dalton25da4062021-07-13 14:06:28 -06002223 SkScalar geoDx = 1.f / SkScalarSqrt(a * a + c * c);
2224 SkScalar geoDy = 1.f / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002225
Brian Salomon05441c42017-05-15 16:45:49 -04002226 fEllipses.emplace_back(
2227 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2228 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
Chris Dalton25da4062021-07-13 14:06:28 -06002229 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
2230 params.fCenter.fY - params.fYRadius,
2231 params.fCenter.fX + params.fXRadius,
2232 params.fCenter.fY + params.fYRadius)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002233 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002234 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002235 }
2236
Brian Salomon289e3d82016-12-14 15:52:56 -05002237 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002238
Robert Phillips294723d2021-06-17 09:23:58 -04002239 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002240 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002241 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002242 } else {
2243 fHelper.visitProxies(func);
2244 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002245 }
2246
Chris Dalton57ab06c2021-04-22 12:57:28 -06002247 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2248 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002249 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2250 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002251 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002252 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002253 GrProcessorAnalysisCoverage::kSingleChannel, color,
2254 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002255 }
2256
2257 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2258
bsalomone46f9fe2015-08-18 06:05:14 -07002259private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002260 GrProgramInfo* programInfo() override { return fProgramInfo; }
2261
Robert Phillips4133dc42020-03-11 15:55:55 -04002262 void onCreateProgramInfo(const GrCaps* caps,
2263 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002264 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002265 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002266 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002267 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002268 GrXferBarrierFlags renderPassXferBarriers,
2269 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002270 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2271 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002272 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002273
Chris Dalton25da4062021-07-13 14:06:28 -06002274 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
2275 if (usesMSAASurface) {
2276 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
2277 }
2278
2279 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
2280 std::move(appliedClip),
2281 dstProxyView, gp,
2282 fHelper.detachProcessorSet(),
2283 GrPrimitiveType::kTriangles,
2284 renderPassXferBarriers,
2285 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05002286 }
2287
Robert Phillips71143952021-06-17 14:55:07 -04002288 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002289 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002290 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002291 }
2292
Robert Phillips787fd9d2021-03-22 14:48:09 -04002293 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002294 GrVertexWriter verts{helper.vertices()};
2295 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002296 return;
2297 }
2298
Brian Salomon05441c42017-05-15 16:45:49 -04002299 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002300 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002301 SkScalar xRadius = ellipse.fXRadius;
2302 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002303
Chris Dalton25da4062021-07-13 14:06:28 -06002304 // On MSAA, bloat enough to guarantee any pixel that might be touched by the ellipse has
2305 // full sample coverage.
2306 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
2307 SkRect drawBounds = ellipse.fBounds.makeOutset(ellipse.fGeoDx * aaBloat,
2308 ellipse.fGeoDy * aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08002309
Chris Dalton25da4062021-07-13 14:06:28 -06002310 // Normalize the "outer radius" coordinates within drawBounds so that the outer edge
2311 // occurs at x^2 + y^2 == 1.
2312 float outerCoordX = drawBounds.width() / (xRadius * 2);
2313 float outerCoordY = drawBounds.height() / (yRadius * 2);
joshualitt76e7fb62015-02-11 08:52:27 -08002314
Chris Dalton25da4062021-07-13 14:06:28 -06002315 // By default, constructed so that inner coord is (0, 0) for all points
2316 float innerCoordX = 0;
2317 float innerCoordY = 0;
2318
2319 // ... unless we're stroked. Then normalize the "inner radius" coordinates within
2320 // drawBounds so that the inner edge occurs at x2^2 + y2^2 == 1.
Greg Daniel75a13022018-04-04 08:59:20 -04002321 if (DIEllipseStyle::kStroke == this->style()) {
Chris Dalton25da4062021-07-13 14:06:28 -06002322 innerCoordX = drawBounds.width() / (ellipse.fInnerXRadius * 2);
2323 innerCoordY = drawBounds.height() / (ellipse.fInnerYRadius * 2);
Greg Daniel75a13022018-04-04 08:59:20 -04002324 }
joshualitt76e7fb62015-02-11 08:52:27 -08002325
Chris Dalton25da4062021-07-13 14:06:28 -06002326 verts.writeQuad(GrVertexWriter::TriStripFromRect(drawBounds),
Brian Osman2b6e3902018-11-21 15:29:43 -05002327 color,
Chris Dalton25da4062021-07-13 14:06:28 -06002328 origin_centered_tri_strip(outerCoordX, outerCoordY),
Brian Osman788b9162020-02-07 10:36:46 -05002329 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Chris Dalton25da4062021-07-13 14:06:28 -06002330 origin_centered_tri_strip(innerCoordX, innerCoordY));
joshualitt76e7fb62015-02-11 08:52:27 -08002331 }
Robert Phillips4490d922020-03-03 14:50:59 -05002332 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002333 }
2334
2335 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002336 if (!fProgramInfo || !fMesh) {
2337 return;
2338 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002339
Chris Dalton765ed362020-03-16 17:34:44 -06002340 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002341 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002342 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002343 }
halcanary9d524f22016-03-29 09:03:52 -07002344
Herb Derbye25c3002020-10-27 15:57:27 -04002345 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002346 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002347 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002348 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002349 }
2350
bsalomoncdaa97b2016-03-08 08:30:14 -08002351 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002352 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002353 }
2354
joshualittd96a67b2015-05-05 14:09:05 -07002355 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002356 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002357 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002358 }
2359
Brian Salomon05441c42017-05-15 16:45:49 -04002360 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002361 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002362 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002363 }
2364
John Stilesaf366522020-08-13 09:57:34 -04002365#if GR_TEST_UTILS
2366 SkString onDumpInfo() const override {
2367 SkString string;
2368 for (const auto& geo : fEllipses) {
2369 string.appendf(
2370 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2371 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2372 "GeoDY: %.2f\n",
2373 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2374 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2375 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2376 }
2377 string += fHelper.dumpInfo();
2378 return string;
2379 }
2380#endif
2381
Brian Salomon05441c42017-05-15 16:45:49 -04002382 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2383 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002384
Brian Salomon05441c42017-05-15 16:45:49 -04002385 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002386 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002387 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002388 SkScalar fXRadius;
2389 SkScalar fYRadius;
2390 SkScalar fInnerXRadius;
2391 SkScalar fInnerYRadius;
2392 SkScalar fGeoDx;
2393 SkScalar fGeoDy;
2394 DIEllipseStyle fStyle;
2395 SkRect fBounds;
2396 };
2397
Brian Salomon05441c42017-05-15 16:45:49 -04002398 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002399 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002400 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002401 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002402
Chris Daltoneb694b72020-03-16 09:25:50 -06002403 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002404 GrProgramInfo* fProgramInfo = nullptr;
2405
John Stiles7571f9e2020-09-02 22:42:33 -04002406 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002407};
2408
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409///////////////////////////////////////////////////////////////////////////////
2410
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002411// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002412//
2413// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2414// ____________
2415// |_|________|_|
2416// | | | |
2417// | | | |
2418// | | | |
2419// |_|________|_|
2420// |_|________|_|
2421//
2422// For strokes, we don't draw the center quad.
2423//
2424// For circular roundrects, in the case where the stroke width is greater than twice
2425// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002426// in the center. The shared vertices are duplicated so we can set a different outer radius
2427// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002428// ____________
2429// |_|________|_|
2430// | |\ ____ /| |
2431// | | | | | |
2432// | | |____| | |
2433// |_|/______\|_|
2434// |_|________|_|
2435//
2436// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002437//
2438// For filled rrects that need to provide a distance vector we resuse the overstroke
2439// geometry but make the inner rect degenerate (either a point or a horizontal or
2440// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002441
jvanverth84839f62016-08-29 10:16:40 -07002442static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002443 // clang-format off
2444 // overstroke quads
2445 // we place this at the beginning so that we can skip these indices when rendering normally
2446 16, 17, 19, 16, 19, 18,
2447 19, 17, 23, 19, 23, 21,
2448 21, 23, 22, 21, 22, 20,
2449 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002450
Brian Salomon289e3d82016-12-14 15:52:56 -05002451 // corners
2452 0, 1, 5, 0, 5, 4,
2453 2, 3, 7, 2, 7, 6,
2454 8, 9, 13, 8, 13, 12,
2455 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002456
Brian Salomon289e3d82016-12-14 15:52:56 -05002457 // edges
2458 1, 2, 6, 1, 6, 5,
2459 4, 5, 9, 4, 9, 8,
2460 6, 7, 11, 6, 11, 10,
2461 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002462
Brian Salomon289e3d82016-12-14 15:52:56 -05002463 // center
2464 // we place this at the end so that we can ignore these indices when not rendering as filled
2465 5, 6, 10, 5, 10, 9,
2466 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002467};
Brian Salomon289e3d82016-12-14 15:52:56 -05002468
jvanverth84839f62016-08-29 10:16:40 -07002469// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002470static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002471
jvanverth84839f62016-08-29 10:16:40 -07002472// overstroke count is arraysize minus the center indices
2473static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2474// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002475static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002476// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002477static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2478static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002479static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002480
jvanverthc3d0e422016-08-25 08:12:35 -07002481enum RRectType {
2482 kFill_RRectType,
2483 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002484 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002485};
2486
jvanverth84839f62016-08-29 10:16:40 -07002487static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002488 switch (type) {
2489 case kFill_RRectType:
2490 case kStroke_RRectType:
2491 return kVertsPerStandardRRect;
2492 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002493 return kVertsPerOverstrokeRRect;
2494 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002495 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002496}
2497
2498static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002499 switch (type) {
2500 case kFill_RRectType:
2501 return kIndicesPerFillRRect;
2502 case kStroke_RRectType:
2503 return kIndicesPerStrokeRRect;
2504 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002505 return kIndicesPerOverstrokeRRect;
2506 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002507 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002508}
2509
2510static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002511 switch (type) {
2512 case kFill_RRectType:
2513 case kStroke_RRectType:
2514 return gStandardRRectIndices;
2515 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002516 return gOverstrokeRRectIndices;
2517 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002518 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002519}
2520
joshualitt76e7fb62015-02-11 08:52:27 -08002521///////////////////////////////////////////////////////////////////////////////////////////////////
2522
Robert Phillips79839d42016-10-06 15:03:34 -04002523// For distance computations in the interior of filled rrects we:
2524//
2525// add a interior degenerate (point or line) rect
2526// each vertex of that rect gets -outerRad as its radius
2527// this makes the computation of the distance to the outer edge be negative
2528// negative values are caught and then handled differently in the GP's onEmitCode
2529// each vertex is also given the normalized x & y distance from the interior rect's edge
2530// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2531
Brian Salomon05441c42017-05-15 16:45:49 -04002532class CircularRRectOp : public GrMeshDrawOp {
2533private:
2534 using Helper = GrSimpleMeshDrawOpHelper;
2535
joshualitt76e7fb62015-02-11 08:52:27 -08002536public:
Brian Salomon25a88092016-12-01 09:36:50 -05002537 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002538
bsalomon4b4a7cc2016-07-08 04:42:54 -07002539 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2540 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002541 static GrOp::Owner Make(GrRecordingContext* context,
2542 GrPaint&& paint,
2543 const SkMatrix& viewMatrix,
2544 const SkRect& devRect,
2545 float devRadius,
2546 float devStrokeWidth,
2547 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002548 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002549 devRect, devRadius,
2550 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002551 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002552 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002553 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2554 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002555 : INHERITED(ClassID())
2556 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002557 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002558 SkRect bounds = devRect;
2559 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2560 SkScalar innerRadius = 0.0f;
2561 SkScalar outerRadius = devRadius;
2562 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002563 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002564 if (devStrokeWidth > 0) {
2565 if (SkScalarNearlyZero(devStrokeWidth)) {
2566 halfWidth = SK_ScalarHalf;
2567 } else {
2568 halfWidth = SkScalarHalf(devStrokeWidth);
2569 }
joshualitt76e7fb62015-02-11 08:52:27 -08002570
bsalomon4b4a7cc2016-07-08 04:42:54 -07002571 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002572 // Outset stroke by 1/4 pixel
2573 devStrokeWidth += 0.25f;
2574 // If stroke is greater than width or height, this is still a fill
2575 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002576 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002577 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002578 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002579 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002580 }
2581 outerRadius += halfWidth;
2582 bounds.outset(halfWidth, halfWidth);
2583 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002584
Greg Daniel2655ede2019-04-10 00:49:28 +00002585 // The radii are outset for two reasons. First, it allows the shader to simply perform
2586 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2587 // Second, the outer radius is used to compute the verts of the bounding box that is
2588 // rendered and the outset ensures the box will cover all partially covered by the rrect
2589 // corners.
2590 outerRadius += SK_ScalarHalf;
2591 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002592
Greg Daniel5faf4742019-10-01 15:14:44 -04002593 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002594
Greg Daniel2655ede2019-04-10 00:49:28 +00002595 // Expand the rect for aa to generate correct vertices.
2596 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002597
Brian Salomon05441c42017-05-15 16:45:49 -04002598 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002599 fVertCount = rrect_type_to_vert_count(type);
2600 fIndexCount = rrect_type_to_index_count(type);
2601 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002602 }
2603
Brian Salomon289e3d82016-12-14 15:52:56 -05002604 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002605
Robert Phillips294723d2021-06-17 09:23:58 -04002606 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002607 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002608 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002609 } else {
2610 fHelper.visitProxies(func);
2611 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002612 }
2613
Chris Dalton57ab06c2021-04-22 12:57:28 -06002614 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2615 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002616 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002617 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002618 GrProcessorAnalysisCoverage::kSingleChannel, color,
2619 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002620 }
2621
2622 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2623
Brian Salomon92aee3d2016-12-21 09:20:25 -05002624private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002625 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002626 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002627 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002628 SkASSERT(smInset < bigInset);
2629
2630 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002631 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2632 color,
2633 xOffset, 0.0f,
2634 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002635
2636 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002637 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2638 color,
2639 xOffset, 0.0f,
2640 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002641
Brian Osmana1d4eb92018-12-06 16:33:10 -05002642 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2643 color,
2644 0.0f, 0.0f,
2645 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002646
Brian Osmana1d4eb92018-12-06 16:33:10 -05002647 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2648 color,
2649 0.0f, 0.0f,
2650 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002651
Brian Osmana1d4eb92018-12-06 16:33:10 -05002652 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2653 color,
2654 0.0f, 0.0f,
2655 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002656
Brian Osmana1d4eb92018-12-06 16:33:10 -05002657 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2658 color,
2659 0.0f, 0.0f,
2660 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002661
2662 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002663 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2664 color,
2665 xOffset, 0.0f,
2666 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002667
2668 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002669 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2670 color,
2671 xOffset, 0.0f,
2672 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002673 }
2674
Robert Phillips2669a7b2020-03-12 12:07:19 -04002675 GrProgramInfo* programInfo() override { return fProgramInfo; }
2676
Robert Phillips4133dc42020-03-11 15:55:55 -04002677 void onCreateProgramInfo(const GrCaps* caps,
2678 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002679 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002680 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002681 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002682 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002683 GrXferBarrierFlags renderPassXferBarriers,
2684 GrLoadOp colorLoadOp) override {
Chris Dalton25da4062021-07-13 14:06:28 -06002685 SkASSERT(!usesMSAASurface);
2686
bsalomoncdaa97b2016-03-08 08:30:14 -08002687 // Invert the view matrix as a local matrix (if any other processors require coords).
2688 SkMatrix localMatrix;
2689 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002690 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002691 }
2692
Robert Phillips4490d922020-03-03 14:50:59 -05002693 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002694 false, false, false, false,
2695 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002696
Brian Salomon8afde5f2020-04-01 16:22:00 -04002697 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002698 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002699 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002700 }
2701
Robert Phillips71143952021-06-17 14:55:07 -04002702 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002703 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002704 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002705 if (!fProgramInfo) {
2706 return;
2707 }
2708 }
2709
Brian Salomon12d22642019-01-29 14:38:50 -05002710 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002711 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002712
Robert Phillips787fd9d2021-03-22 14:48:09 -04002713 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002714 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002715 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002716 SkDebugf("Could not allocate vertices\n");
2717 return;
2718 }
2719
Brian Salomon12d22642019-01-29 14:38:50 -05002720 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002721 int firstIndex = 0;
2722 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2723 if (!indices) {
2724 SkDebugf("Could not allocate indices\n");
2725 return;
2726 }
2727
2728 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002729 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002730 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002731 SkScalar outerRadius = rrect.fOuterRadius;
2732 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002733
Brian Salomon289e3d82016-12-14 15:52:56 -05002734 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2735 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002736
Brian Salomon289e3d82016-12-14 15:52:56 -05002737 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002738 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002739 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002740 SkScalar innerRadius = rrect.fType != kFill_RRectType
2741 ? rrect.fInnerRadius / rrect.fOuterRadius
2742 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002743 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002744 verts.write(bounds.fLeft, yCoords[i],
2745 color,
2746 -1.0f, yOuterRadii[i],
2747 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002748
Brian Osmana1d4eb92018-12-06 16:33:10 -05002749 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2750 color,
2751 0.0f, yOuterRadii[i],
2752 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002753
Brian Osmana1d4eb92018-12-06 16:33:10 -05002754 verts.write(bounds.fRight - outerRadius, yCoords[i],
2755 color,
2756 0.0f, yOuterRadii[i],
2757 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002758
Brian Osmana1d4eb92018-12-06 16:33:10 -05002759 verts.write(bounds.fRight, yCoords[i],
2760 color,
2761 1.0f, yOuterRadii[i],
2762 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002763 }
jvanverthc3d0e422016-08-25 08:12:35 -07002764 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002765 // Effectively this is an additional stroked rrect, with its
2766 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2767 // This will give us correct AA in the center and the correct
2768 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002769 //
jvanvertha4f1af82016-08-29 07:17:47 -07002770 // Also, the outer offset is a constant vector pointing to the right, which
2771 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002772 if (kOverstroke_RRectType == rrect.fType) {
2773 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002774
Brian Salomon05441c42017-05-15 16:45:49 -04002775 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002776 // this is the normalized distance from the outer rectangle of this
2777 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002778 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002779
Brian Osmana1d4eb92018-12-06 16:33:10 -05002780 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002781 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002782 }
jvanverth6a397612016-08-26 08:15:33 -07002783
Brian Salomon05441c42017-05-15 16:45:49 -04002784 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2785 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002786 for (int i = 0; i < primIndexCount; ++i) {
2787 *indices++ = primIndices[i] + currStartVertex;
2788 }
2789
Brian Salomon05441c42017-05-15 16:45:49 -04002790 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002791 }
2792
Robert Phillips4490d922020-03-03 14:50:59 -05002793 fMesh = target->allocMesh();
2794 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002795 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002796 }
2797
2798 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002799 if (!fProgramInfo || !fMesh) {
2800 return;
2801 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002802
Chris Dalton765ed362020-03-16 17:34:44 -06002803 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002804 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002805 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002806 }
2807
Herb Derbye25c3002020-10-27 15:57:27 -04002808 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002809 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002810
2811 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002812 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002813 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002814 }
2815
Brian Salomon05441c42017-05-15 16:45:49 -04002816 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002817 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002818 }
2819
Brian Salomon05441c42017-05-15 16:45:49 -04002820 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002821 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2822 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002823 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002824 }
2825
Brian Salomon05441c42017-05-15 16:45:49 -04002826 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002827 fVertCount += that->fVertCount;
2828 fIndexCount += that->fIndexCount;
2829 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002830 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002831 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002832 }
2833
John Stilesaf366522020-08-13 09:57:34 -04002834#if GR_TEST_UTILS
2835 SkString onDumpInfo() const override {
2836 SkString string;
2837 for (int i = 0; i < fRRects.count(); ++i) {
2838 string.appendf(
2839 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2840 "InnerRad: %.2f, OuterRad: %.2f\n",
2841 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2842 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2843 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2844 fRRects[i].fOuterRadius);
2845 }
2846 string += fHelper.dumpInfo();
2847 return string;
2848 }
2849#endif
2850
Brian Salomon05441c42017-05-15 16:45:49 -04002851 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002852 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002853 SkScalar fInnerRadius;
2854 SkScalar fOuterRadius;
2855 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002856 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002857 };
2858
Brian Salomon289e3d82016-12-14 15:52:56 -05002859 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002860 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002861 int fVertCount;
2862 int fIndexCount;
2863 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002864 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002865 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002866
Chris Daltoneb694b72020-03-16 09:25:50 -06002867 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002868 GrProgramInfo* fProgramInfo = nullptr;
2869
John Stiles7571f9e2020-09-02 22:42:33 -04002870 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002871};
2872
jvanverth84839f62016-08-29 10:16:40 -07002873static const int kNumRRectsInIndexBuffer = 256;
2874
2875GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2876GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002877static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2878 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002879 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2880 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2881 switch (type) {
2882 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002883 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002884 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2885 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002886 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002887 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002888 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2889 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002890 default:
2891 SkASSERT(false);
2892 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002893 }
jvanverth84839f62016-08-29 10:16:40 -07002894}
2895
Brian Salomon05441c42017-05-15 16:45:49 -04002896class EllipticalRRectOp : public GrMeshDrawOp {
2897private:
2898 using Helper = GrSimpleMeshDrawOpHelper;
2899
joshualitt76e7fb62015-02-11 08:52:27 -08002900public:
Brian Salomon25a88092016-12-01 09:36:50 -05002901 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002902
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2904 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002905 static GrOp::Owner Make(GrRecordingContext* context,
2906 GrPaint&& paint,
2907 const SkMatrix& viewMatrix,
2908 const SkRect& devRect,
2909 float devXRadius,
2910 float devYRadius,
2911 SkVector devStrokeWidths,
2912 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002913 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2914 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002915 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2916 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002917 if (devStrokeWidths.fX > 0) {
2918 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2919 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2920 } else {
2921 devStrokeWidths.scale(SK_ScalarHalf);
2922 }
joshualitt76e7fb62015-02-11 08:52:27 -08002923
bsalomon4b4a7cc2016-07-08 04:42:54 -07002924 // we only handle thick strokes for near-circular ellipses
2925 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002926 (SK_ScalarHalf * devXRadius > devYRadius ||
2927 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002928 return nullptr;
2929 }
2930
2931 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002932 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2933 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002934 return nullptr;
2935 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002936 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2937 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002938 return nullptr;
2939 }
Brian Salomon05441c42017-05-15 16:45:49 -04002940 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002941 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002942 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002943 devXRadius, devYRadius, devStrokeWidths,
2944 strokeOnly);
2945 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002946
Herb Derbyc76d4092020-10-07 16:46:15 -04002947 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002948 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2949 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002950 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002951 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002952 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002953 SkScalar innerXRadius = 0.0f;
2954 SkScalar innerYRadius = 0.0f;
2955 SkRect bounds = devRect;
2956 bool stroked = false;
2957 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002958 // this is legit only if scale & translation (which should be the case at the moment)
2959 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002960 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2961 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002962 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2963 }
2964
Brian Salomon05441c42017-05-15 16:45:49 -04002965 devXRadius += devStrokeHalfWidths.fX;
2966 devYRadius += devStrokeHalfWidths.fY;
2967 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002968 }
2969
Brian Salomon05441c42017-05-15 16:45:49 -04002970 fStroked = stroked;
2971 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002972 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04002973 fRRects.emplace_back(
2974 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002975 }
2976
Brian Salomon289e3d82016-12-14 15:52:56 -05002977 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002978
Robert Phillips294723d2021-06-17 09:23:58 -04002979 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002980 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002981 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002982 } else {
2983 fHelper.visitProxies(func);
2984 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002985 }
2986
Chris Dalton57ab06c2021-04-22 12:57:28 -06002987 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2988 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002989 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002990 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002991 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002992 GrProcessorAnalysisCoverage::kSingleChannel, color,
2993 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002994 }
2995
2996 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2997
bsalomone46f9fe2015-08-18 06:05:14 -07002998private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002999 GrProgramInfo* programInfo() override { return fProgramInfo; }
3000
Robert Phillips4133dc42020-03-11 15:55:55 -04003001 void onCreateProgramInfo(const GrCaps* caps,
3002 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05003003 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06003004 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04003005 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04003006 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05003007 GrXferBarrierFlags renderPassXferBarriers,
3008 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08003009 SkMatrix localMatrix;
3010 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04003011 return;
joshualitt76e7fb62015-02-11 08:52:27 -08003012 }
3013
Robert Phillips4490d922020-03-03 14:50:59 -05003014 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
3015 fUseScale, localMatrix);
3016
Chris Dalton25da4062021-07-13 14:06:28 -06003017 GrPipeline::InputFlags pipelineFlags = fHelper.pipelineFlags();
3018 if (usesMSAASurface) {
3019 pipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
3020 }
3021
3022 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
3023 std::move(appliedClip),
3024 dstProxyView, gp,
3025 fHelper.detachProcessorSet(),
3026 GrPrimitiveType::kTriangles,
3027 renderPassXferBarriers,
3028 colorLoadOp, pipelineFlags);
Robert Phillips4490d922020-03-03 14:50:59 -05003029 }
3030
Robert Phillips71143952021-06-17 14:55:07 -04003031 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003032 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04003033 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05003034 if (!fProgramInfo) {
3035 return;
3036 }
3037 }
joshualitt76e7fb62015-02-11 08:52:27 -08003038
bsalomonb5238a72015-05-05 07:49:49 -07003039 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07003040 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04003041 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3042 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003043
Brian Salomon12d22642019-01-29 14:38:50 -05003044 if (!indexBuffer) {
3045 SkDebugf("Could not allocate indices\n");
3046 return;
3047 }
Robert Phillips4490d922020-03-03 14:50:59 -05003048 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003049 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003050 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003051 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003052 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003053 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003054 SkDebugf("Could not allocate vertices\n");
3055 return;
3056 }
3057
Brian Salomon05441c42017-05-15 16:45:49 -04003058 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003059 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003060 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003061 float reciprocalRadii[4] = {
3062 SkScalarInvert(rrect.fXRadius),
3063 SkScalarInvert(rrect.fYRadius),
3064 SkScalarInvert(rrect.fInnerXRadius),
3065 SkScalarInvert(rrect.fInnerYRadius)
3066 };
joshualitt76e7fb62015-02-11 08:52:27 -08003067
Brian Osmane3afdd52020-10-28 10:49:56 -04003068 // If the stroke width is exactly double the radius, the inner radii will be zero.
3069 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3070 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3071 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3072
Chris Dalton25da4062021-07-13 14:06:28 -06003073 // On MSAA, bloat enough to guarantee any pixel that might be touched by the rrect has
3074 // full sample coverage.
3075 float aaBloat = target->usesMSAASurface() ? SK_ScalarSqrt2 : .5f;
3076
3077 // Extend out the radii to antialias.
3078 SkScalar xOuterRadius = rrect.fXRadius + aaBloat;
3079 SkScalar yOuterRadius = rrect.fYRadius + aaBloat;
joshualitt76e7fb62015-02-11 08:52:27 -08003080
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003081 SkScalar xMaxOffset = xOuterRadius;
3082 SkScalar yMaxOffset = yOuterRadius;
3083 if (!fStroked) {
3084 // For filled rrects we map a unit circle in the vertex attributes rather than
3085 // computing an ellipse and modifying that distance, so we normalize to 1.
3086 xMaxOffset /= rrect.fXRadius;
3087 yMaxOffset /= rrect.fYRadius;
3088 }
3089
Chris Dalton25da4062021-07-13 14:06:28 -06003090 const SkRect& bounds = rrect.fDevBounds.makeOutset(aaBloat, aaBloat);
joshualitt76e7fb62015-02-11 08:52:27 -08003091
Brian Salomon289e3d82016-12-14 15:52:56 -05003092 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3093 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003094 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003095 SK_ScalarNearlyZero, // we're using inversesqrt() in
3096 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003097 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003098
Brian Osman788b9162020-02-07 10:36:46 -05003099 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003100 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003101 verts.write(bounds.fLeft, yCoords[i],
3102 color,
3103 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003104 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003105 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003106
Brian Osmana1d4eb92018-12-06 16:33:10 -05003107 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3108 color,
3109 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003110 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003111 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003112
Brian Osmana1d4eb92018-12-06 16:33:10 -05003113 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3114 color,
3115 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003116 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003117 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003118
Brian Osmana1d4eb92018-12-06 16:33:10 -05003119 verts.write(bounds.fRight, yCoords[i],
3120 color,
3121 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003122 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003123 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003124 }
3125 }
Robert Phillips4490d922020-03-03 14:50:59 -05003126 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003127 }
3128
3129 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003130 if (!fProgramInfo || !fMesh) {
3131 return;
3132 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003133
Chris Dalton765ed362020-03-16 17:34:44 -06003134 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003135 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003136 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003137 }
3138
Herb Derbye25c3002020-10-27 15:57:27 -04003139 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003140 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003141
Brian Salomon05441c42017-05-15 16:45:49 -04003142 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003143 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003144 }
3145
bsalomoncdaa97b2016-03-08 08:30:14 -08003146 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003147 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003148 }
3149
Brian Salomon05441c42017-05-15 16:45:49 -04003150 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003151 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3152 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003153 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003154 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003155
Brian Salomon05441c42017-05-15 16:45:49 -04003156 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003157 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003158 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003159 }
3160
John Stilesaf366522020-08-13 09:57:34 -04003161#if GR_TEST_UTILS
3162 SkString onDumpInfo() const override {
3163 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3164 for (const auto& geo : fRRects) {
3165 string.appendf(
3166 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3167 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3168 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3169 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3170 geo.fInnerXRadius, geo.fInnerYRadius);
3171 }
3172 string += fHelper.dumpInfo();
3173 return string;
3174 }
3175#endif
3176
Brian Salomon05441c42017-05-15 16:45:49 -04003177 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003178 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003179 SkScalar fXRadius;
3180 SkScalar fYRadius;
3181 SkScalar fInnerXRadius;
3182 SkScalar fInnerYRadius;
3183 SkRect fDevBounds;
3184 };
3185
Brian Salomon289e3d82016-12-14 15:52:56 -05003186 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003187 Helper fHelper;
3188 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003189 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003190 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003191 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003192
Chris Daltoneb694b72020-03-16 09:25:50 -06003193 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003194 GrProgramInfo* fProgramInfo = nullptr;
3195
John Stiles7571f9e2020-09-02 22:42:33 -04003196 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003197};
3198
Herb Derbyc76d4092020-10-07 16:46:15 -04003199GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3200 GrPaint&& paint,
3201 const SkMatrix& viewMatrix,
3202 const SkRRect& rrect,
3203 const SkStrokeRec& stroke,
3204 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003205 SkASSERT(viewMatrix.rectStaysRect());
3206 SkASSERT(viewMatrix.isSimilarity());
3207 SkASSERT(rrect.isSimple());
3208 SkASSERT(!rrect.isOval());
3209 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3210
3211 // RRect ops only handle simple, but not too simple, rrects.
3212 // Do any matrix crunching before we reset the draw state for device coords.
3213 const SkRect& rrectBounds = rrect.getBounds();
3214 SkRect bounds;
3215 viewMatrix.mapRect(&bounds, rrectBounds);
3216
3217 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3218 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3219 viewMatrix[SkMatrix::kMSkewY]));
3220
3221 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3222 SkScalar scaledStroke = -1;
3223 SkScalar strokeWidth = stroke.getWidth();
3224 SkStrokeRec::Style style = stroke.getStyle();
3225
3226 bool isStrokeOnly =
3227 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3228 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3229
3230 if (hasStroke) {
3231 if (SkStrokeRec::kHairline_Style == style) {
3232 scaledStroke = SK_Scalar1;
3233 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003234 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3235 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003236 }
3237 }
3238
3239 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3240 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3241 // patch will have fractional coverage. This only matters when the interior is actually filled.
3242 // We could consider falling back to rect rendering here, since a tiny radius is
3243 // indistinguishable from a square corner.
3244 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3245 return nullptr;
3246 }
3247
3248 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3249 scaledStroke, isStrokeOnly);
3250}
3251
Herb Derbyc76d4092020-10-07 16:46:15 -04003252GrOp::Owner make_rrect_op(GrRecordingContext* context,
3253 GrPaint&& paint,
3254 const SkMatrix& viewMatrix,
3255 const SkRRect& rrect,
3256 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003257 SkASSERT(viewMatrix.rectStaysRect());
3258 SkASSERT(rrect.isSimple());
3259 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003260
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003261 // RRect ops only handle simple, but not too simple, rrects.
3262 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003263 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003264 SkRect bounds;
3265 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003266
Mike Reed242135a2018-02-22 13:41:39 -05003267 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003268 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3269 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3270 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3271 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003272
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003273 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003274
bsalomon4b4a7cc2016-07-08 04:42:54 -07003275 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3276 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003277 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003278
Brian Salomon289e3d82016-12-14 15:52:56 -05003279 bool isStrokeOnly =
3280 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003281 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3282
3283 if (hasStroke) {
3284 if (SkStrokeRec::kHairline_Style == style) {
3285 scaledStroke.set(1, 1);
3286 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003287 scaledStroke.fX = SkScalarAbs(
3288 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3289 scaledStroke.fY = SkScalarAbs(
3290 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003291 }
3292
Jim Van Verth64b85892019-06-17 12:01:46 -04003293 // if half of strokewidth is greater than radius, we don't handle that right now
3294 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3295 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003296 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003297 }
3298 }
3299
Brian Salomon8a97f562019-04-18 14:07:27 -04003300 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003301 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003302 std::swap(xRadius, yRadius);
3303 std::swap(scaledStroke.fX, scaledStroke.fY);
3304 }
3305
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003306 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3307 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3308 // patch will have fractional coverage. This only matters when the interior is actually filled.
3309 // We could consider falling back to rect rendering here, since a tiny radius is
3310 // indistinguishable from a square corner.
3311 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003312 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003313 }
3314
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003315 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003316 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3317 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003318}
3319
Herb Derbyc76d4092020-10-07 16:46:15 -04003320GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3321 GrPaint&& paint,
3322 const SkMatrix& viewMatrix,
3323 const SkRRect& rrect,
3324 const SkStrokeRec& stroke,
3325 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003326 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003327 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003328 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003329 }
3330
3331 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003332 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003333 }
3334
Greg Daniel2655ede2019-04-10 00:49:28 +00003335 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003336}
joshualitt3e708c52015-04-30 13:49:27 -07003337
bsalomon4b4a7cc2016-07-08 04:42:54 -07003338///////////////////////////////////////////////////////////////////////////////
3339
Herb Derbyc76d4092020-10-07 16:46:15 -04003340GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3341 GrPaint&& paint,
3342 const SkMatrix& viewMatrix,
3343 const SkRect& oval,
3344 const GrStyle& style,
3345 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003346 SkScalar width = oval.width();
3347 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3348 circle_stays_circle(viewMatrix));
3349
3350 auto r = width / 2.f;
3351 SkPoint center = { oval.centerX(), oval.centerY() };
3352 if (style.hasNonDashPathEffect()) {
3353 return nullptr;
3354 } else if (style.isDashed()) {
3355 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3356 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3357 return nullptr;
3358 }
3359 auto onInterval = style.dashIntervals()[0];
3360 auto offInterval = style.dashIntervals()[1];
3361 if (offInterval == 0) {
3362 GrStyle strokeStyle(style.strokeRec(), nullptr);
3363 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3364 strokeStyle, shaderCaps);
3365 } else if (onInterval == 0) {
3366 // There is nothing to draw but we have no way to indicate that here.
3367 return nullptr;
3368 }
3369 auto angularOnInterval = onInterval / r;
3370 auto angularOffInterval = offInterval / r;
3371 auto phaseAngle = style.dashPhase() / r;
3372 // Currently this function doesn't accept ovals with different start angles, though
3373 // it could.
3374 static const SkScalar kStartAngle = 0.f;
3375 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3376 style.strokeRec().getWidth(), kStartAngle,
3377 angularOnInterval, angularOffInterval, phaseAngle);
3378 }
3379 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3380}
3381
Herb Derbyc76d4092020-10-07 16:46:15 -04003382GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3383 GrPaint&& paint,
3384 const SkMatrix& viewMatrix,
3385 const SkRect& oval,
3386 const GrStyle& style,
3387 const GrShaderCaps* shaderCaps) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003388 if (style.pathEffect()) {
3389 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003390 }
3391
Stan Ilieveb868aa2017-02-21 11:06:16 -05003392 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003393 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003394 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003395 }
3396
Stan Ilieveb868aa2017-02-21 11:06:16 -05003397 // Otherwise, if we have shader derivative support, render as device-independent
3398 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003399 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3400 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3401 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3402 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3403 // Check for near-degenerate matrix
3404 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003405 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003406 style.strokeRec());
3407 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003408 }
3409
bsalomon4b4a7cc2016-07-08 04:42:54 -07003410 return nullptr;
3411}
3412
3413///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003414
Herb Derbyc76d4092020-10-07 16:46:15 -04003415GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3416 GrPaint&& paint,
3417 const SkMatrix& viewMatrix,
3418 const SkRect& oval, SkScalar startAngle,
3419 SkScalar sweepAngle, bool useCenter,
3420 const GrStyle& style,
3421 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003422 SkASSERT(!oval.isEmpty());
3423 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003424 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003425 if (SkScalarAbs(sweepAngle) >= 360.f) {
3426 return nullptr;
3427 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003428 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3429 return nullptr;
3430 }
3431 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003432 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3433 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003434 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003435 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003436}
3437
3438///////////////////////////////////////////////////////////////////////////////
3439
Hal Canary6f6961e2017-01-31 13:50:44 -05003440#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003441
Brian Salomon05441c42017-05-15 16:45:49 -04003442GR_DRAW_OP_TEST_DEFINE(CircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003443 if (numSamples > 1) {
3444 return nullptr;
3445 }
3446
bsalomon4f3a0ca2016-08-22 13:14:26 -07003447 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003448 SkScalar rotate = random->nextSScalar1() * 360.f;
3449 SkScalar translateX = random->nextSScalar1() * 1000.f;
3450 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003451 SkScalar scale;
3452 do {
3453 scale = random->nextSScalar1() * 100.f;
3454 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003455 SkMatrix viewMatrix;
3456 viewMatrix.setRotate(rotate);
3457 viewMatrix.postTranslate(translateX, translateY);
3458 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003459 SkRect circle = GrTest::TestSquare(random);
3460 SkPoint center = {circle.centerX(), circle.centerY()};
3461 SkScalar radius = circle.width() / 2.f;
3462 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003463 CircleOp::ArcParams arcParamsTmp;
3464 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003465 if (random->nextBool()) {
3466 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003467 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3468 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003469 arcParams = &arcParamsTmp;
3470 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003471 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3472 center, radius,
3473 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003474 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003475 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003476 }
Mike Klein16885072018-12-11 09:54:31 -05003477 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003478 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003479}
3480
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003481GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
Robert Phillipscc6e50f2021-07-22 16:41:32 -04003482 if (numSamples > 1) {
3483 return nullptr;
3484 }
3485
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003486 SkScalar rotate = random->nextSScalar1() * 360.f;
3487 SkScalar translateX = random->nextSScalar1() * 1000.f;
3488 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003489 SkScalar scale;
3490 do {
3491 scale = random->nextSScalar1() * 100.f;
3492 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003493 SkMatrix viewMatrix;
3494 viewMatrix.setRotate(rotate);
3495 viewMatrix.postTranslate(translateX, translateY);
3496 viewMatrix.postScale(scale, scale);
3497 SkRect circle = GrTest::TestSquare(random);
3498 SkPoint center = {circle.centerX(), circle.centerY()};
3499 SkScalar radius = circle.width() / 2.f;
3500 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3501 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3502 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3503 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3504 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003505 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3506 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003507 startAngle, onAngle, offAngle, phase);
3508}
3509
Brian Salomon05441c42017-05-15 16:45:49 -04003510GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003511 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003512 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003513 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003514 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003515}
3516
Brian Salomon05441c42017-05-15 16:45:49 -04003517GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003518 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003519 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003520 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003521 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003522}
3523
Jim Van Verth64b85892019-06-17 12:01:46 -04003524GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3525 do {
3526 SkScalar rotate = random->nextSScalar1() * 360.f;
3527 SkScalar translateX = random->nextSScalar1() * 1000.f;
3528 SkScalar translateY = random->nextSScalar1() * 1000.f;
3529 SkScalar scale;
3530 do {
3531 scale = random->nextSScalar1() * 100.f;
3532 } while (scale == 0);
3533 SkMatrix viewMatrix;
3534 viewMatrix.setRotate(rotate);
3535 viewMatrix.postTranslate(translateX, translateY);
3536 viewMatrix.postScale(scale, scale);
3537 SkRect rect = GrTest::TestRect(random);
3538 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3539 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3540 if (rrect.isOval()) {
3541 continue;
3542 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003543 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003544 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3545 GrTest::TestStrokeRec(random), nullptr);
3546 if (op) {
3547 return op;
3548 }
3549 assert_alive(paint);
3550 } while (true);
3551}
3552
Brian Salomon05441c42017-05-15 16:45:49 -04003553GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003554 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003555 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003556 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003557 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003558}
3559
3560#endif