blob: 2fac6d8b1b4e102f85ed9a41f93bc9f499bf5a81 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
78 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
79 GLSLProcessor::GenKey(*this, caps, b);
80 }
81
Robert Phillipsf10535f2021-03-23 09:30:45 -040082 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050083 return new GLSLProcessor();
84 }
85
86private:
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400119 const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500155 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500156 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800157
joshualittabb52a12015-01-13 15:02:10 -0800158 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400159 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
160 WriteLocalCoord(vertBuilder,
161 uniformHandler,
162 *args.fShaderCaps,
163 gpArgs,
164 cgp.fInPosition.asShaderVar(),
165 cgp.fLocalMatrix,
166 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800167
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400168 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500169 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000170 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800171 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500172 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500173 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000174 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800175 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000177
Brian Salomon92be2f72018-06-19 14:33:47 -0400178 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500179 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000180 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
181 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
185 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400187 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500188 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000189 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
190 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700191 }
192 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400193 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400194 // We compute coverage of the round caps as circles at the butt caps produced
195 // by the clip planes. The inverse of the clip planes is applied so that there
196 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400197 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500198 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
199 " roundCapCenters.xy)));"
200 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
201 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400202 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400203 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
204 capRadius.fsIn(), capRadius.fsIn());
205 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700206 }
John Stiles4d7ac492021-03-09 20:16:43 -0500207 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208 }
209
robertphillips46d36f02015-01-18 08:14:14 -0800210 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400211 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700212 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800213 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500214 b->addBool(cgp.fStroke, "stroked");
215 b->addBool(cgp.fInClipPlane.isInitialized(), "clipPlane");
216 b->addBool(cgp.fInIsectPlane.isInitialized(), "isectPlane");
217 b->addBool(cgp.fInUnionPlane.isInitialized(), "unionPlane");
218 b->addBool(cgp.fInRoundCapCenters.isInitialized(), "roundCapCenters");
Brian Salomon5a328282021-04-14 10:32:25 -0400219 b->addBits(kMatrixKeyBits,
220 ComputeMatrixKey(shaderCaps, cgp.fLocalMatrix),
221 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222 }
223
Brian Osman609f1592020-07-01 15:14:39 -0400224 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400225 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400226 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400227 SetTransform(pdman,
228 shaderCaps,
229 fLocalMatrixUniform,
230 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
231 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700232 }
233
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400235 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400236
237 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
238 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239 };
240
Brian Salomon289e3d82016-12-14 15:52:56 -0500241 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400242
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500243 Attribute fInPosition;
244 Attribute fInColor;
245 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400246 // Optional attributes.
247 Attribute fInClipPlane;
248 Attribute fInIsectPlane;
249 Attribute fInUnionPlane;
250 Attribute fInRoundCapCenters;
251
Brian Salomon289e3d82016-12-14 15:52:56 -0500252 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400253 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254
John Stiles7571f9e2020-09-02 22:42:33 -0400255 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256};
257
bsalomoncdaa97b2016-03-08 08:30:14 -0800258GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000259
Hal Canary6f6961e2017-01-31 13:50:44 -0500260#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400262 bool stroke = d->fRandom->nextBool();
263 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500264 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400265 bool clipPlane = d->fRandom->nextBool();
266 bool isectPlane = d->fRandom->nextBool();
267 bool unionPlane = d->fRandom->nextBool();
268 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
270 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271}
Hal Canary6f6961e2017-01-31 13:50:44 -0500272#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
275public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500276 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
277 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600278 return arena->make([&](void* ptr) {
279 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
280 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400281 }
282
283 ~ButtCapDashedCircleGeometryProcessor() override {}
284
285 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
286
287 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
288 GLSLProcessor::GenKey(*this, caps, b);
289 }
290
Robert Phillipsf10535f2021-03-23 09:30:45 -0400291 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400292 return new GLSLProcessor();
293 }
294
295private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500296 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
297 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
298 , fLocalMatrix(localMatrix) {
299 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
300 fInColor = MakeColorAttribute("inColor", wideColor);
301 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
302 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
303 this->setVertexAttributes(&fInPosition, 4);
304 }
305
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400306 class GLSLProcessor : public GrGLSLGeometryProcessor {
307 public:
308 GLSLProcessor() {}
309
310 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
311 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400312 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400313 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
314 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
315 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
316 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
317
318 // emit attributes
319 varyingHandler->emitAttributes(bcscgp);
320 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500321 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400322
323 fragBuilder->codeAppend("float4 dashParams;");
324 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
327 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
328 varyingHandler->addVarying("wrapDashes", &wrapDashes,
329 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
330 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
331 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500333 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400334 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
335 // x = length of on interval, y = length of on + off.
336 // There are two other parameters in dashParams.zw:
337 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
338 // Each interval has a "corresponding" dash which may be shifted partially or
339 // fully out of its interval by the phase. So there may be up to two "visual"
340 // dashes in an interval.
341 // When computing coverage in an interval we look at three dashes. These are the
342 // "corresponding" dashes from the current, previous, and next intervals. Any of these
343 // may be phase shifted into our interval or even when phase=0 they may be within half a
344 // pixel distance of a pixel center in the interval.
345 // When in the first interval we need to check the dash from the last interval. And
346 // similarly when in the last interval we need to check the dash from the first
347 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
348 // We compute the dash begin/end angles in the vertex shader and apply them in the
349 // fragment shader when we detect we're in the first/last interval.
350 vertBuilder->codeAppend(R"(
351 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
352 // to the fragment shader as a varying.
353 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 // We can happen to be perfectly divisible.
356 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 }
359 // Let 'l' be the last interval before reaching 2 pi.
360 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
361 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
362 // interval.
363 half offset = 0;
364 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500365 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400366 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500367 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400368 }
369 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
370 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
371 // min.
372 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
373
374 // Based on the phase determine whether the -1st, 0th, or 1st interval's
375 // "corresponding" dash appears in the 0th interval and is closest to l.
376 offset = 0;
377 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500378 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400379 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 }
382 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
383 wrapDashes.w = wrapDashes.z + dashParams.x;
384 // The start of the dash we're considering may be clipped by the start of the
385 // circle.
386 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
387 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500388 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
390 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
391 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
392
393 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500394 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500396 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400397 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
398
399 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400400 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
401 WriteLocalCoord(vertBuilder,
402 uniformHandler,
403 *args.fShaderCaps,
404 gpArgs,
405 bcscgp.fInPosition.asShaderVar(),
406 bcscgp.fLocalMatrix,
407 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400409 GrShaderVar fnArgs[] = {
410 GrShaderVar("angleToEdge", kFloat_GrSLType),
411 GrShaderVar("diameter", kFloat_GrSLType),
412 };
John Stiles6b58a332020-10-26 17:53:06 -0400413 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
414 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400415 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 float linearDist;
417 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
418 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400419 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400420 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 fragBuilder->codeAppend(R"(
422 float d = length(circleEdge.xy) * circleEdge.z;
423
424 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500427 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400428 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400429 edgeAlpha *= innerAlpha;
430
Ethan Nicholase1f55022019-02-05 17:17:40 -0500431 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 angleFromStart = mod(angleFromStart, 6.28318530718);
433 float x = mod(angleFromStart, dashParams.y);
434 // Convert the radial distance from center to pixel into a diameter.
435 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
437 half(dashParams.w));
438 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
439 half(dashParams.y) + half(dashParams.x) -
440 half(dashParams.w));
441 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
442 half(-dashParams.y) + half(dashParams.x) -
443 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400444 half dashAlpha = 0;
445 )");
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.y = min(currDash.y, lastIntervalLength);
450 if (nextDash.x >= lastIntervalLength) {
451 // The next dash is outside the 0..2pi range, throw it away
452 nextDash.xy = half2(1000);
453 } else {
454 // Clip the end of the next dash to the end of the circle
455 nextDash.y = min(nextDash.y, lastIntervalLength);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
460 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400462 currDash.x = max(currDash.x, 0);
463 if (prevDash.y <= 0) {
464 // The previous dash is outside the 0..2pi range, throw it away
465 prevDash.xy = half2(1000);
466 } else {
467 // Clip the start previous dash to the start of the circle
468 prevDash.x = max(prevDash.x, 0);
469 }
470 }
471 )", fnName.c_str(), fnName.c_str());
472 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500473 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
474 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
475 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 dashAlpha = min(dashAlpha, 1);
477 edgeAlpha *= dashAlpha;
478 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
479 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500480 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400481 }
482
483 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400484 const GrShaderCaps& shaderCaps,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 GrProcessorKeyBuilder* b) {
486 const ButtCapDashedCircleGeometryProcessor& bcscgp =
487 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400488 b->addBits(kMatrixKeyBits,
489 ComputeMatrixKey(shaderCaps, bcscgp.fLocalMatrix),
490 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 }
492
Brian Osman609f1592020-07-01 15:14:39 -0400493 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400494 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400495 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400496 SetTransform(pdman,
497 shaderCaps,
498 fLocalMatrixUniform,
499 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
500 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501 }
502
503 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400504 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400505
506 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
507 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 };
509
510 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500511 Attribute fInPosition;
512 Attribute fInColor;
513 Attribute fInCircleEdge;
514 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400515
516 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
517
John Stiles7571f9e2020-09-02 22:42:33 -0400518 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400519};
520
521#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500522GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500523 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400524 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400526}
527#endif
528
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000529///////////////////////////////////////////////////////////////////////////////
530
531/**
532 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000533 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
534 * in both x and y directions.
535 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000536 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000537 */
538
bsalomoncdaa97b2016-03-08 08:30:14 -0800539class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000540public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500541 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
542 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600543 return arena->make([&](void* ptr) {
544 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
545 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500546 }
547
548 ~EllipseGeometryProcessor() override {}
549
550 const char* name() const override { return "EllipseGeometryProcessor"; }
551
552 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
553 GLSLProcessor::GenKey(*this, caps, b);
554 }
555
Robert Phillipsf10535f2021-03-23 09:30:45 -0400556 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500557 return new GLSLProcessor();
558 }
559
560private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000561 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400562 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500563 : INHERITED(kEllipseGeometryProcessor_ClassID)
564 , fLocalMatrix(localMatrix)
565 , fStroke(stroke)
566 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500567 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500568 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400569 if (useScale) {
570 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
571 } else {
572 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
573 }
574 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500575 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576 }
577
egdaniel57d3b032015-11-13 11:57:27 -0800578 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800580 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581
Brian Salomon289e3d82016-12-14 15:52:56 -0500582 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400583 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800584 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800585 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800586 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000587
joshualittabb52a12015-01-13 15:02:10 -0800588 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800589 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800590
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400591 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
592 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800593 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800594 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500595 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000596
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400597 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800598 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500599 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800600
Chris Dalton60283612018-02-14 13:38:14 -0700601 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700602 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500603 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500604 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800605
joshualittabb52a12015-01-13 15:02:10 -0800606 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400607 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
608 WriteLocalCoord(vertBuilder,
609 uniformHandler,
610 *args.fShaderCaps,
611 gpArgs,
612 egp.fInPosition.asShaderVar(),
613 egp.fLocalMatrix,
614 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800615
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400616 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
617 // to compute both the edges because we need two separate test equations for
618 // the single offset.
619 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
620 // the distance by the gradient, non-uniformly scaled by the inverse of the
621 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800622
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400623 // On medium precision devices, we scale the denominator of the distance equation
624 // before taking the inverse square root to minimize the chance that we're dividing
625 // by zero, then we scale the result back.
626
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000627 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400628 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400629 if (egp.fStroke) {
630 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
631 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400632 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
633 if (egp.fUseScale) {
634 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
635 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
636 } else {
637 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
638 }
639 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700640
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000641 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400642 if (args.fShaderCaps->floatIs32Bits()) {
643 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
644 } else {
645 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
646 }
647 if (egp.fUseScale) {
648 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
649 ellipseOffsets.fsIn());
650 } else {
651 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
652 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000653 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000655 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800656 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400657 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800658 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400659 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400660 if (egp.fUseScale) {
661 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
662 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
663 } else {
664 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
665 }
666 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
667 if (!args.fShaderCaps->floatIs32Bits()) {
668 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
669 }
670 if (egp.fUseScale) {
671 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
672 ellipseOffsets.fsIn());
673 } else {
674 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
675 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000676 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677 }
678
John Stiles4d7ac492021-03-09 20:16:43 -0500679 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 }
681
robertphillips46d36f02015-01-18 08:14:14 -0800682 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400683 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700684 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800685 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500686 b->addBool(egp.fStroke, "stroked");
Brian Salomon5a328282021-04-14 10:32:25 -0400687 b->addBits(kMatrixKeyBits,
688 ComputeMatrixKey(shaderCaps, egp.fLocalMatrix),
689 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690 }
691
Brian Osman609f1592020-07-01 15:14:39 -0400692 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400693 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400694 const GrGeometryProcessor& geomProc) override {
695 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400696 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700697 }
698
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400700 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400701
702 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
703 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704 };
705
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500706 Attribute fInPosition;
707 Attribute fInColor;
708 Attribute fInEllipseOffset;
709 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400710
joshualitte3ababe2015-05-15 07:56:07 -0700711 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000712 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400713 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000714
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400715 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000716
John Stiles7571f9e2020-09-02 22:42:33 -0400717 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000718};
719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000721
Hal Canary6f6961e2017-01-31 13:50:44 -0500722#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500723GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400724 bool stroke = d->fRandom->nextBool();
725 bool wideColor = d->fRandom->nextBool();
726 bool useScale = d->fRandom->nextBool();
727 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
728 return EllipseGeometryProcessor::Make(d->allocator(), stroke, wideColor, useScale, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000729}
Hal Canary6f6961e2017-01-31 13:50:44 -0500730#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000731
732///////////////////////////////////////////////////////////////////////////////
733
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000734/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000735 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000736 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
737 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
738 * using differentials.
739 *
740 * The result is device-independent and can be used with any affine matrix.
741 */
742
bsalomoncdaa97b2016-03-08 08:30:14 -0800743enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000744
bsalomoncdaa97b2016-03-08 08:30:14 -0800745class DIEllipseGeometryProcessor : public GrGeometryProcessor {
746public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500747 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
748 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600749 return arena->make([&](void* ptr) {
750 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
751 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500752 }
753
754 ~DIEllipseGeometryProcessor() override {}
755
756 const char* name() const override { return "DIEllipseGeometryProcessor"; }
757
758 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
759 GLSLProcessor::GenKey(*this, caps, b);
760 }
761
Robert Phillipsf10535f2021-03-23 09:30:45 -0400762 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500763 return new GLSLProcessor();
764 }
765
766private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000767 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400768 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400769 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400770 , fViewMatrix(viewMatrix)
771 , fUseScale(useScale)
772 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500773 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500774 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400775 if (useScale) {
776 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
777 kFloat3_GrSLType};
778 } else {
779 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
780 kFloat2_GrSLType};
781 }
782 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000784 }
785
egdaniel57d3b032015-11-13 11:57:27 -0800786 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000787 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500788 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000789
joshualitt465283c2015-09-11 08:19:35 -0700790 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400791 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800792 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800793 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800794 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000795
joshualittabb52a12015-01-13 15:02:10 -0800796 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800797 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800798
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
800 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800801 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500802 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700803
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400804 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800805 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500806 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800807
Chris Dalton60283612018-02-14 13:38:14 -0700808 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500809 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500810 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800811
joshualittabb52a12015-01-13 15:02:10 -0800812 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400813 WriteOutputPosition(vertBuilder,
814 uniformHandler,
815 *args.fShaderCaps,
816 gpArgs,
817 diegp.fInPosition.name(),
818 diegp.fViewMatrix,
819 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400820 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800821
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400823 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
824 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
825 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
826 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500827 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400828 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
829 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500830 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400831 if (diegp.fUseScale) {
832 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
833 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000834
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000836 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400837 if (args.fShaderCaps->floatIs32Bits()) {
838 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
839 } else {
840 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
841 }
842 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
843 if (diegp.fUseScale) {
844 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
845 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800846 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000848 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
849 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000851 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000852 }
853
854 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800855 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800856 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
857 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400858 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
859 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500860 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400861 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
862 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500863 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400864 if (diegp.fUseScale) {
865 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
866 }
867 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
868 if (!args.fShaderCaps->floatIs32Bits()) {
869 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
870 }
871 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
872 if (diegp.fUseScale) {
873 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
874 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000875 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000876 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000877
John Stiles4d7ac492021-03-09 20:16:43 -0500878 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879 }
880
robertphillips46d36f02015-01-18 08:14:14 -0800881 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400882 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700883 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800884 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500885 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
Brian Salomon5a328282021-04-14 10:32:25 -0400886 b->addBits(kMatrixKeyBits,
887 ComputeMatrixKey(shaderCaps, diegp.fViewMatrix),
888 "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889 }
890
Brian Osman609f1592020-07-01 15:14:39 -0400891 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400892 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400893 const GrGeometryProcessor& geomProc) override {
894 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700895
Brian Salomon5a328282021-04-14 10:32:25 -0400896 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897 }
898
899 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400900 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700901 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800902
John Stiles7571f9e2020-09-02 22:42:33 -0400903 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000904 };
905
Brian Salomon92be2f72018-06-19 14:33:47 -0400906
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500907 Attribute fInPosition;
908 Attribute fInColor;
909 Attribute fInEllipseOffsets0;
910 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400911
Brian Salomon289e3d82016-12-14 15:52:56 -0500912 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400913 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400916 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000917
John Stiles7571f9e2020-09-02 22:42:33 -0400918 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000919};
920
bsalomoncdaa97b2016-03-08 08:30:14 -0800921GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000922
Hal Canary6f6961e2017-01-31 13:50:44 -0500923#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500924GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
John Stiles391a9f62021-04-29 23:17:41 -0400925 bool wideColor = d->fRandom->nextBool();
926 bool useScale = d->fRandom->nextBool();
927 SkMatrix matrix = GrTest::TestMatrix(d->fRandom);
928 auto style = (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2));
929 return DIEllipseGeometryProcessor::Make(d->allocator(), wideColor, useScale, matrix, style);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000930}
Hal Canary6f6961e2017-01-31 13:50:44 -0500931#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000932
933///////////////////////////////////////////////////////////////////////////////
934
jvanverth6ca48822016-10-07 06:57:32 -0700935// We have two possible cases for geometry for a circle:
936
937// In the case of a normal fill, we draw geometry for the circle as an octagon.
938static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500939 // enter the octagon
940 // clang-format off
941 0, 1, 8, 1, 2, 8,
942 2, 3, 8, 3, 4, 8,
943 4, 5, 8, 5, 6, 8,
944 6, 7, 8, 7, 0, 8
945 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700946};
947
948// For stroked circles, we use two nested octagons.
949static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500950 // enter the octagon
951 // clang-format off
952 0, 1, 9, 0, 9, 8,
953 1, 2, 10, 1, 10, 9,
954 2, 3, 11, 2, 11, 10,
955 3, 4, 12, 3, 12, 11,
956 4, 5, 13, 4, 13, 12,
957 5, 6, 14, 5, 14, 13,
958 6, 7, 15, 6, 15, 14,
959 7, 0, 8, 7, 8, 15,
960 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700961};
962
Brian Osman9d958b52018-11-13 12:46:56 -0500963// Normalized geometry for octagons that circumscribe and lie on a circle:
964
965static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
966static constexpr SkPoint kOctagonOuter[] = {
967 SkPoint::Make(-kOctOffset, -1),
968 SkPoint::Make( kOctOffset, -1),
969 SkPoint::Make( 1, -kOctOffset),
970 SkPoint::Make( 1, kOctOffset),
971 SkPoint::Make( kOctOffset, 1),
972 SkPoint::Make(-kOctOffset, 1),
973 SkPoint::Make(-1, kOctOffset),
974 SkPoint::Make(-1, -kOctOffset),
975};
976
977// cosine and sine of pi/8
978static constexpr SkScalar kCosPi8 = 0.923579533f;
979static constexpr SkScalar kSinPi8 = 0.382683432f;
980static constexpr SkPoint kOctagonInner[] = {
981 SkPoint::Make(-kSinPi8, -kCosPi8),
982 SkPoint::Make( kSinPi8, -kCosPi8),
983 SkPoint::Make( kCosPi8, -kSinPi8),
984 SkPoint::Make( kCosPi8, kSinPi8),
985 SkPoint::Make( kSinPi8, kCosPi8),
986 SkPoint::Make(-kSinPi8, kCosPi8),
987 SkPoint::Make(-kCosPi8, kSinPi8),
988 SkPoint::Make(-kCosPi8, -kSinPi8),
989};
Brian Salomon289e3d82016-12-14 15:52:56 -0500990
jvanverth6ca48822016-10-07 06:57:32 -0700991static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
992static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
993static const int kVertsPerStrokeCircle = 16;
994static const int kVertsPerFillCircle = 9;
995
996static int circle_type_to_vert_count(bool stroked) {
997 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
998}
999
1000static int circle_type_to_index_count(bool stroked) {
1001 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
1002}
1003
1004static const uint16_t* circle_type_to_indices(bool stroked) {
1005 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1006}
1007
1008///////////////////////////////////////////////////////////////////////////////
1009
Brian Salomon05441c42017-05-15 16:45:49 -04001010class CircleOp final : public GrMeshDrawOp {
1011private:
1012 using Helper = GrSimpleMeshDrawOpHelper;
1013
joshualitt76e7fb62015-02-11 08:52:27 -08001014public:
Brian Salomon25a88092016-12-01 09:36:50 -05001015 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001016
bsalomon4f3a0ca2016-08-22 13:14:26 -07001017 /** Optional extra params to render a partial arc rather than a full circle. */
1018 struct ArcParams {
1019 SkScalar fStartAngleRadians;
1020 SkScalar fSweepAngleRadians;
1021 bool fUseCenter;
1022 };
Brian Salomon05441c42017-05-15 16:45:49 -04001023
Herb Derbyc76d4092020-10-07 16:46:15 -04001024 static GrOp::Owner Make(GrRecordingContext* context,
1025 GrPaint&& paint,
1026 const SkMatrix& viewMatrix,
1027 SkPoint center,
1028 SkScalar radius,
1029 const GrStyle& style,
1030 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001032 if (style.hasPathEffect()) {
1033 return nullptr;
1034 }
Brian Salomon05441c42017-05-15 16:45:49 -04001035 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001036 SkStrokeRec::Style recStyle = stroke.getStyle();
1037 if (arcParams) {
1038 // Arc support depends on the style.
1039 switch (recStyle) {
1040 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001041 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001042 return nullptr;
1043 case SkStrokeRec::kFill_Style:
1044 // This supports all fills.
1045 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001046 case SkStrokeRec::kStroke_Style:
1047 // Strokes that don't use the center point are supported with butt and round
1048 // caps.
1049 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1050 return nullptr;
1051 }
1052 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001053 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001054 // Hairline only supports butt cap. Round caps could be emulated by slightly
1055 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001056 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1057 return nullptr;
1058 }
1059 break;
1060 }
1061 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001062 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1063 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001064 }
1065
Herb Derbyc76d4092020-10-07 16:46:15 -04001066 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001067 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1068 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001069 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001070 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001071 const SkStrokeRec& stroke = style.strokeRec();
1072 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073
Brian Salomon45c92202018-04-10 10:53:58 -04001074 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001075
bsalomon4b4a7cc2016-07-08 04:42:54 -07001076 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001077 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001078 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001079
Brian Salomon289e3d82016-12-14 15:52:56 -05001080 bool isStrokeOnly =
1081 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001082 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001083
jvanverth6ca48822016-10-07 06:57:32 -07001084 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001085 SkScalar outerRadius = radius;
1086 SkScalar halfWidth = 0;
1087 if (hasStroke) {
1088 if (SkScalarNearlyZero(strokeWidth)) {
1089 halfWidth = SK_ScalarHalf;
1090 } else {
1091 halfWidth = SkScalarHalf(strokeWidth);
1092 }
1093
1094 outerRadius += halfWidth;
1095 if (isStrokeOnly) {
1096 innerRadius = radius - halfWidth;
1097 }
1098 }
1099
1100 // The radii are outset for two reasons. First, it allows the shader to simply perform
1101 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1102 // Second, the outer radius is used to compute the verts of the bounding box that is
1103 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001104 outerRadius += SK_ScalarHalf;
1105 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001106 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001107 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001108
bsalomon4f3a0ca2016-08-22 13:14:26 -07001109 // This makes every point fully inside the intersection plane.
1110 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1111 // This makes every point fully outside the union plane.
1112 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001113 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001114 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1115 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001116 if (arcParams) {
1117 // The shader operates in a space where the circle is translated to be centered at the
1118 // origin. Here we compute points on the unit circle at the starting and ending angles.
1119 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001120 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1121 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001123 stopPoint.fY = SkScalarSin(endAngle);
1124 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001125
1126 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1127 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1128 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1129 startPoint.normalize();
1130 stopPoint.normalize();
1131
Brian Salomon3517aa72019-12-11 08:16:22 -05001132 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1133 // should orient the clip planes for arcs.
1134 SkASSERT(viewMatrix.isSimilarity());
1135 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1136 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1137 if (upperLeftDet < 0) {
1138 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001139 }
1140
Brian Salomon45c92202018-04-10 10:53:58 -04001141 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1142 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1143 SkPoint roundCaps[2];
1144 if (fRoundCaps) {
1145 // Compute the cap center points in the normalized space.
1146 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1147 roundCaps[0] = startPoint * midRadius;
1148 roundCaps[1] = stopPoint * midRadius;
1149 } else {
1150 roundCaps[0] = kUnusedRoundCaps[0];
1151 roundCaps[1] = kUnusedRoundCaps[1];
1152 }
1153
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001155 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1156 // center of the butts.
1157 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001158 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001159 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001160 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001161 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1162 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1163 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001164 if (useCenter) {
1165 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1166 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001167 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1168 if (arcParams->fSweepAngleRadians < 0) {
1169 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001170 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001171 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001172 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001173 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001174 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001175 color,
1176 innerRadius,
1177 outerRadius,
1178 {norm0.fX, norm0.fY, 0.5f},
1179 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1180 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001181 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001182 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001183 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001184 fClipPlaneIsect = false;
1185 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001186 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001187 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001188 color,
1189 innerRadius,
1190 outerRadius,
1191 {norm0.fX, norm0.fY, 0.5f},
1192 {norm1.fX, norm1.fY, 0.5f},
1193 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001194 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001195 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001196 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001197 fClipPlaneIsect = true;
1198 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001199 }
1200 } else {
1201 // We clip to a secant of the original circle.
1202 startPoint.scale(radius);
1203 stopPoint.scale(radius);
1204 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1205 norm.normalize();
1206 if (arcParams->fSweepAngleRadians > 0) {
1207 norm.negate();
1208 }
1209 SkScalar d = -norm.dot(startPoint) + 0.5f;
1210
Brian Salomon05441c42017-05-15 16:45:49 -04001211 fCircles.emplace_back(
1212 Circle{color,
1213 innerRadius,
1214 outerRadius,
1215 {norm.fX, norm.fY, d},
1216 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1217 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001218 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001219 devBounds,
1220 stroked});
1221 fClipPlane = true;
1222 fClipPlaneIsect = false;
1223 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001224 }
1225 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001226 fCircles.emplace_back(
1227 Circle{color,
1228 innerRadius,
1229 outerRadius,
1230 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1231 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1232 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001233 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001234 devBounds,
1235 stroked});
1236 fClipPlane = false;
1237 fClipPlaneIsect = false;
1238 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001239 }
bsalomon88cf17d2016-07-08 06:40:56 -07001240 // Use the original radius and stroke radius for the bounds so that it does not include the
1241 // AA bloat.
1242 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001243 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001244 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001245 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001246 fVertCount = circle_type_to_vert_count(stroked);
1247 fIndexCount = circle_type_to_index_count(stroked);
1248 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001250
Brian Salomon289e3d82016-12-14 15:52:56 -05001251 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001252
Robert Phillips294723d2021-06-17 09:23:58 -04001253 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001254 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001255 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001256 } else {
1257 fHelper.visitProxies(func);
1258 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001259 }
1260
Chris Dalton57ab06c2021-04-22 12:57:28 -06001261 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1262 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001263 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001264 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001265 GrProcessorAnalysisCoverage::kSingleChannel, color,
1266 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001267 }
1268
1269 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1270
bsalomone46f9fe2015-08-18 06:05:14 -07001271private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001272 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001273
Robert Phillips4133dc42020-03-11 15:55:55 -04001274 void onCreateProgramInfo(const GrCaps* caps,
1275 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001276 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001277 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001278 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001279 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001280 GrXferBarrierFlags renderPassXferBarriers,
1281 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001282 SkMatrix localMatrix;
1283 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001284 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001285 }
1286
Robert Phillips4490d922020-03-03 14:50:59 -05001287 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001288 fClipPlaneIsect, fClipPlaneUnion,
1289 fRoundCaps, fWideColor,
1290 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001291
Brian Salomon8afde5f2020-04-01 16:22:00 -04001292 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001293 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001294 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001295 }
1296
Robert Phillips71143952021-06-17 14:55:07 -04001297 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001298 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001299 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001300 if (!fProgramInfo) {
1301 return;
1302 }
1303 }
1304
Brian Salomon12d22642019-01-29 14:38:50 -05001305 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001306 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001307 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001308 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001309 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001310 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001311 return;
1312 }
1313
Brian Salomon12d22642019-01-29 14:38:50 -05001314 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001315 int firstIndex = 0;
1316 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1317 if (!indices) {
1318 SkDebugf("Could not allocate indices\n");
1319 return;
1320 }
1321
1322 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001323 for (const auto& circle : fCircles) {
1324 SkScalar innerRadius = circle.fInnerRadius;
1325 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001326 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001327 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001328
joshualitt76e7fb62015-02-11 08:52:27 -08001329 // The inner radius in the vertex data must be specified in normalized space.
1330 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001331 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001332
1333 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001334 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001335
Brian Osman9a24fee2018-08-03 09:48:42 -04001336 SkVector geoClipPlane = { 0, 0 };
1337 SkScalar offsetClipDist = SK_Scalar1;
1338 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1339 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1340 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1341 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1342 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1343 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1344 // the AA can extend just past the center of the circle.
1345 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1346 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1347 SkAssertResult(geoClipPlane.normalize());
1348 offsetClipDist = 0.5f / halfWidth;
1349 }
1350
Brian Osman7d8f82b2018-11-08 10:24:09 -05001351 for (int i = 0; i < 8; ++i) {
1352 // This clips the normalized offset to the half-plane we computed above. Then we
1353 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001354 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001355 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(center + offset * halfWidth,
1357 color,
1358 offset,
1359 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001360 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001361 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001362 }
1363 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001365 }
1366 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001367 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001368 }
1369 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001370 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001371 }
Brian Salomon45c92202018-04-10 10:53:58 -04001372 }
jvanverth6ca48822016-10-07 06:57:32 -07001373
Brian Salomon05441c42017-05-15 16:45:49 -04001374 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001375 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001376
Brian Osman7d8f82b2018-11-08 10:24:09 -05001377 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001378 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1379 color,
1380 kOctagonInner[i] * innerRadius,
1381 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001382 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001383 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001384 }
1385 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001386 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001387 }
1388 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001389 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001390 }
1391 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001392 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001393 }
Brian Salomon45c92202018-04-10 10:53:58 -04001394 }
jvanverth6ca48822016-10-07 06:57:32 -07001395 } else {
1396 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001397 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001398 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001399 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001400 }
jvanverth6ca48822016-10-07 06:57:32 -07001401 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001402 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001403 }
1404 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001405 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001406 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001407 if (fRoundCaps) {
1408 vertices.write(circle.fRoundCapCenters);
1409 }
jvanverth6ca48822016-10-07 06:57:32 -07001410 }
1411
Brian Salomon05441c42017-05-15 16:45:49 -04001412 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1413 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001414 for (int i = 0; i < primIndexCount; ++i) {
1415 *indices++ = primIndices[i] + currStartVertex;
1416 }
1417
Brian Salomon05441c42017-05-15 16:45:49 -04001418 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001419 }
jvanverth6ca48822016-10-07 06:57:32 -07001420
Robert Phillips4490d922020-03-03 14:50:59 -05001421 fMesh = target->allocMesh();
1422 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001423 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001424 }
1425
1426 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001427 if (!fProgramInfo || !fMesh) {
1428 return;
1429 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001430
Chris Dalton765ed362020-03-16 17:34:44 -06001431 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001432 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001433 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001434 }
1435
Herb Derbye25c3002020-10-27 15:57:27 -04001436 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001437 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001438
1439 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001440 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001441 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001442 }
1443
Brian Salomon05441c42017-05-15 16:45:49 -04001444 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001445 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001446 }
1447
Brian Salomon05441c42017-05-15 16:45:49 -04001448 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001449 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1450 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001451 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001452 }
1453
Brian Salomon289e3d82016-12-14 15:52:56 -05001454 // Because we've set up the ops that don't use the planes with noop values
1455 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001456 fClipPlane |= that->fClipPlane;
1457 fClipPlaneIsect |= that->fClipPlaneIsect;
1458 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001459 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001460 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001461
Brian Salomon05441c42017-05-15 16:45:49 -04001462 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001463 fVertCount += that->fVertCount;
1464 fIndexCount += that->fIndexCount;
1465 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001466 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001467 }
1468
John Stilesaf366522020-08-13 09:57:34 -04001469#if GR_TEST_UTILS
1470 SkString onDumpInfo() const override {
1471 SkString string;
1472 for (int i = 0; i < fCircles.count(); ++i) {
1473 string.appendf(
1474 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1475 "InnerRad: %.2f, OuterRad: %.2f\n",
1476 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1477 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1478 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1479 fCircles[i].fOuterRadius);
1480 }
1481 string += fHelper.dumpInfo();
1482 return string;
1483 }
1484#endif
1485
Brian Salomon05441c42017-05-15 16:45:49 -04001486 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001487 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001488 SkScalar fInnerRadius;
1489 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001490 SkScalar fClipPlane[3];
1491 SkScalar fIsectPlane[3];
1492 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001493 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001494 SkRect fDevBounds;
1495 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001496 };
1497
Brian Salomon289e3d82016-12-14 15:52:56 -05001498 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001499 Helper fHelper;
1500 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001501 int fVertCount;
1502 int fIndexCount;
1503 bool fAllFill;
1504 bool fClipPlane;
1505 bool fClipPlaneIsect;
1506 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001507 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001508 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001509
Chris Daltoneb694b72020-03-16 09:25:50 -06001510 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001511 GrProgramInfo* fProgramInfo = nullptr;
1512
John Stiles7571f9e2020-09-02 22:42:33 -04001513 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001514};
1515
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001516class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1517private:
1518 using Helper = GrSimpleMeshDrawOpHelper;
1519
1520public:
1521 DEFINE_OP_CLASS_ID
1522
Herb Derbyc76d4092020-10-07 16:46:15 -04001523 static GrOp::Owner Make(GrRecordingContext* context,
1524 GrPaint&& paint,
1525 const SkMatrix& viewMatrix,
1526 SkPoint center,
1527 SkScalar radius,
1528 SkScalar strokeWidth,
1529 SkScalar startAngle,
1530 SkScalar onAngle,
1531 SkScalar offAngle,
1532 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001533 SkASSERT(circle_stays_circle(viewMatrix));
1534 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001535 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1536 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001537 onAngle, offAngle, phaseAngle);
1538 }
1539
Herb Derbyc76d4092020-10-07 16:46:15 -04001540 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001541 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1542 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1543 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001544 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001545 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001546 SkASSERT(circle_stays_circle(viewMatrix));
1547 viewMatrix.mapPoints(&center, 1);
1548 radius = viewMatrix.mapRadius(radius);
1549 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1550
1551 // Determine the angle where the circle starts in device space and whether its orientation
1552 // has been reversed.
1553 SkVector start;
1554 bool reflection;
1555 if (!startAngle) {
1556 start = {1, 0};
1557 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001558 start.fY = SkScalarSin(startAngle);
1559 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001560 }
1561 viewMatrix.mapVectors(&start, 1);
1562 startAngle = SkScalarATan2(start.fY, start.fX);
1563 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1564 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1565
1566 auto totalAngle = onAngle + offAngle;
1567 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1568
1569 SkScalar halfWidth = 0;
1570 if (SkScalarNearlyZero(strokeWidth)) {
1571 halfWidth = SK_ScalarHalf;
1572 } else {
1573 halfWidth = SkScalarHalf(strokeWidth);
1574 }
1575
1576 SkScalar outerRadius = radius + halfWidth;
1577 SkScalar innerRadius = radius - halfWidth;
1578
1579 // The radii are outset for two reasons. First, it allows the shader to simply perform
1580 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1581 // Second, the outer radius is used to compute the verts of the bounding box that is
1582 // rendered and the outset ensures the box will cover all partially covered by the circle.
1583 outerRadius += SK_ScalarHalf;
1584 innerRadius -= SK_ScalarHalf;
1585 fViewMatrixIfUsingLocalCoords = viewMatrix;
1586
1587 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1588 center.fX + outerRadius, center.fY + outerRadius);
1589
1590 // We store whether there is a reflection as a negative total angle.
1591 if (reflection) {
1592 totalAngle = -totalAngle;
1593 }
1594 fCircles.push_back(Circle{
1595 color,
1596 outerRadius,
1597 innerRadius,
1598 onAngle,
1599 totalAngle,
1600 startAngle,
1601 phaseAngle,
1602 devBounds
1603 });
1604 // Use the original radius and stroke radius for the bounds so that it does not include the
1605 // AA bloat.
1606 radius += halfWidth;
1607 this->setBounds(
1608 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001609 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001610 fVertCount = circle_type_to_vert_count(true);
1611 fIndexCount = circle_type_to_index_count(true);
1612 }
1613
1614 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1615
Robert Phillips294723d2021-06-17 09:23:58 -04001616 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001617 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001618 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001619 } else {
1620 fHelper.visitProxies(func);
1621 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001622 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001623
Chris Dalton57ab06c2021-04-22 12:57:28 -06001624 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1625 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001626 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001627 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001628 GrProcessorAnalysisCoverage::kSingleChannel, color,
1629 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001630 }
1631
1632 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1633
1634private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001635 GrProgramInfo* programInfo() override { return fProgramInfo; }
1636
Robert Phillips4133dc42020-03-11 15:55:55 -04001637 void onCreateProgramInfo(const GrCaps* caps,
1638 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001639 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06001640 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04001641 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001642 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001643 GrXferBarrierFlags renderPassXferBarriers,
1644 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001645 SkMatrix localMatrix;
1646 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001647 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001648 }
1649
1650 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001651 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001652 fWideColor,
1653 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001654
Brian Salomon8afde5f2020-04-01 16:22:00 -04001655 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001656 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001657 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001658 }
1659
Robert Phillips71143952021-06-17 14:55:07 -04001660 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001661 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001662 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001663 if (!fProgramInfo) {
1664 return;
1665 }
1666 }
1667
Brian Salomon12d22642019-01-29 14:38:50 -05001668 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001669 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001670 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001671 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001672 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001673 SkDebugf("Could not allocate vertices\n");
1674 return;
1675 }
1676
Brian Salomon12d22642019-01-29 14:38:50 -05001677 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001678 int firstIndex = 0;
1679 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1680 if (!indices) {
1681 SkDebugf("Could not allocate indices\n");
1682 return;
1683 }
1684
1685 int currStartVertex = 0;
1686 for (const auto& circle : fCircles) {
1687 // The inner radius in the vertex data must be specified in normalized space so that
1688 // length() can be called with smaller values to avoid precision issues with half
1689 // floats.
1690 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1691 const SkRect& bounds = circle.fDevBounds;
1692 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001693 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1694 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1695 };
1696 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001697 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001698 dashParams.totalAngle = -dashParams.totalAngle;
1699 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700 }
1701
Brian Osmane3caf2d2018-11-21 13:48:36 -05001702 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001703
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001704 // The bounding geometry for the circle is composed of an outer bounding octagon and
1705 // an inner bounded octagon.
1706
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001707 // Compute the vertices of the outer octagon.
1708 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1709 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001710
1711 auto reflectY = [=](const SkPoint& p) {
1712 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001713 };
Brian Osman9d958b52018-11-13 12:46:56 -05001714
1715 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001716 vertices.write(center + kOctagonOuter[i] * halfWidth,
1717 color,
1718 reflectY(kOctagonOuter[i]),
1719 circle.fOuterRadius,
1720 normInnerRadius,
1721 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001722 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723
1724 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001725 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001726 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1727 color,
1728 reflectY(kOctagonInner[i]) * normInnerRadius,
1729 circle.fOuterRadius,
1730 normInnerRadius,
1731 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001732 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001733
1734 const uint16_t* primIndices = circle_type_to_indices(true);
1735 const int primIndexCount = circle_type_to_index_count(true);
1736 for (int i = 0; i < primIndexCount; ++i) {
1737 *indices++ = primIndices[i] + currStartVertex;
1738 }
1739
1740 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 }
1742
Robert Phillips4490d922020-03-03 14:50:59 -05001743 fMesh = target->allocMesh();
1744 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001745 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001746 }
1747
1748 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001749 if (!fProgramInfo || !fMesh) {
1750 return;
1751 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001752
Chris Dalton765ed362020-03-16 17:34:44 -06001753 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001754 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001755 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001756 }
1757
Herb Derbye25c3002020-10-27 15:57:27 -04001758 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001759 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1760
1761 // can only represent 65535 unique vertices with 16-bit indices
1762 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001763 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001764 }
1765
1766 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001767 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001768 }
1769
1770 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001771 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1772 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001773 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001774 }
1775
1776 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001777 fVertCount += that->fVertCount;
1778 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001779 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001780 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001781 }
1782
John Stilesaf366522020-08-13 09:57:34 -04001783#if GR_TEST_UTILS
1784 SkString onDumpInfo() const override {
1785 SkString string;
1786 for (int i = 0; i < fCircles.count(); ++i) {
1787 string.appendf(
1788 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1789 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1790 "Phase: %.2f\n",
1791 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1792 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1793 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1794 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1795 fCircles[i].fPhaseAngle);
1796 }
1797 string += fHelper.dumpInfo();
1798 return string;
1799 }
1800#endif
1801
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001802 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001803 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001804 SkScalar fOuterRadius;
1805 SkScalar fInnerRadius;
1806 SkScalar fOnAngle;
1807 SkScalar fTotalAngle;
1808 SkScalar fStartAngle;
1809 SkScalar fPhaseAngle;
1810 SkRect fDevBounds;
1811 };
1812
1813 SkMatrix fViewMatrixIfUsingLocalCoords;
1814 Helper fHelper;
1815 SkSTArray<1, Circle, true> fCircles;
1816 int fVertCount;
1817 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001818 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001819
Chris Daltoneb694b72020-03-16 09:25:50 -06001820 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001821 GrProgramInfo* fProgramInfo = nullptr;
1822
John Stiles7571f9e2020-09-02 22:42:33 -04001823 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001824};
1825
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001826///////////////////////////////////////////////////////////////////////////////
1827
Brian Salomon05441c42017-05-15 16:45:49 -04001828class EllipseOp : public GrMeshDrawOp {
1829private:
1830 using Helper = GrSimpleMeshDrawOpHelper;
1831
1832 struct DeviceSpaceParams {
1833 SkPoint fCenter;
1834 SkScalar fXRadius;
1835 SkScalar fYRadius;
1836 SkScalar fInnerXRadius;
1837 SkScalar fInnerYRadius;
1838 };
1839
joshualitt76e7fb62015-02-11 08:52:27 -08001840public:
Brian Salomon25a88092016-12-01 09:36:50 -05001841 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001842
Herb Derbyc76d4092020-10-07 16:46:15 -04001843 static GrOp::Owner Make(GrRecordingContext* context,
1844 GrPaint&& paint,
1845 const SkMatrix& viewMatrix,
1846 const SkRect& ellipse,
1847 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001848 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001849 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001850 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1851 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1853 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001854 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1855 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1856 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1857 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001858
bsalomon4b4a7cc2016-07-08 04:42:54 -07001859 // do (potentially) anisotropic mapping of stroke
1860 SkVector scaledStroke;
1861 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001862 scaledStroke.fX = SkScalarAbs(
1863 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1864 scaledStroke.fY = SkScalarAbs(
1865 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001866
1867 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001868 bool isStrokeOnly =
1869 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001870 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1871
Brian Salomon05441c42017-05-15 16:45:49 -04001872 params.fInnerXRadius = 0;
1873 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001874 if (hasStroke) {
1875 if (SkScalarNearlyZero(scaledStroke.length())) {
1876 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1877 } else {
1878 scaledStroke.scale(SK_ScalarHalf);
1879 }
1880
1881 // we only handle thick strokes for near-circular ellipses
1882 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001883 (0.5f * params.fXRadius > params.fYRadius ||
1884 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001885 return nullptr;
1886 }
1887
1888 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001889 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1890 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1891 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1892 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001893 return nullptr;
1894 }
1895
1896 // this is legit only if scale & translation (which should be the case at the moment)
1897 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001898 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1899 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001900 }
1901
Brian Salomon05441c42017-05-15 16:45:49 -04001902 params.fXRadius += scaledStroke.fX;
1903 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001904 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001905
1906 // For large ovals with low precision floats, we fall back to the path renderer.
1907 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1908 // minimum value to avoid divides by zero. With large ovals and low precision this
1909 // leads to blurring at the edge of the oval.
1910 const SkScalar kMaxOvalRadius = 16384;
1911 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1912 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1913 return nullptr;
1914 }
1915
Greg Daniel2655ede2019-04-10 00:49:28 +00001916 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001917 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001918 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001919
Herb Derbyc76d4092020-10-07 16:46:15 -04001920 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001921 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001922 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001923 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001924 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001925 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001926 SkStrokeRec::Style style = stroke.getStyle();
1927 bool isStrokeOnly =
1928 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001929
Brian Salomon05441c42017-05-15 16:45:49 -04001930 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1931 params.fInnerXRadius, params.fInnerYRadius,
1932 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1933 params.fCenter.fY - params.fYRadius,
1934 params.fCenter.fX + params.fXRadius,
1935 params.fCenter.fY + params.fYRadius)});
1936
Greg Daniel5faf4742019-10-01 15:14:44 -04001937 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001938
bsalomon4b4a7cc2016-07-08 04:42:54 -07001939 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001940 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -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
Brian Salomon8afde5f2020-04-01 16:22:00 -04001987 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001988 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001989 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001990 }
1991
Robert Phillips71143952021-06-17 14:55:07 -04001992 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001993 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001994 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001995 if (!fProgramInfo) {
1996 return;
1997 }
1998 }
1999
Robert Phillips787fd9d2021-03-22 14:48:09 -04002000 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002001 GrVertexWriter verts{helper.vertices()};
2002 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05002003 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08002004 return;
2005 }
2006
Brian Salomon05441c42017-05-15 16:45:49 -04002007 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002008 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002009 SkScalar xRadius = ellipse.fXRadius;
2010 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002011
2012 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002013 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2014 SkScalarInvert(xRadius),
2015 SkScalarInvert(yRadius),
2016 SkScalarInvert(ellipse.fInnerXRadius),
2017 SkScalarInvert(ellipse.fInnerYRadius)
2018 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002019 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2020 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002021
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002022 if (!fStroked) {
2023 // For filled ellipses we map a unit circle in the vertex attributes rather than
2024 // computing an ellipse and modifying that distance, so we normalize to 1
2025 xMaxOffset /= xRadius;
2026 yMaxOffset /= yRadius;
2027 }
2028
joshualitt76e7fb62015-02-11 08:52:27 -08002029 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002030 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2031 color,
2032 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002033 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002034 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002035 }
Robert Phillips4490d922020-03-03 14:50:59 -05002036 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002037 }
2038
2039 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002040 if (!fProgramInfo || !fMesh) {
2041 return;
2042 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002043
Chris Dalton765ed362020-03-16 17:34:44 -06002044 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002045 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002046 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002047 }
2048
Herb Derbye25c3002020-10-27 15:57:27 -04002049 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002050 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002051
Brian Salomon05441c42017-05-15 16:45:49 -04002052 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002053 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002054 }
2055
bsalomoncdaa97b2016-03-08 08:30:14 -08002056 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002057 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002058 }
2059
Brian Salomon05441c42017-05-15 16:45:49 -04002060 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002061 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2062 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002063 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002064 }
2065
Brian Salomon05441c42017-05-15 16:45:49 -04002066 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002067 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002068 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002069 }
2070
John Stilesaf366522020-08-13 09:57:34 -04002071#if GR_TEST_UTILS
2072 SkString onDumpInfo() const override {
2073 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2074 for (const auto& geo : fEllipses) {
2075 string.appendf(
2076 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2077 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2078 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2079 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2080 geo.fInnerXRadius, geo.fInnerYRadius);
2081 }
2082 string += fHelper.dumpInfo();
2083 return string;
2084 }
2085#endif
2086
Brian Salomon05441c42017-05-15 16:45:49 -04002087 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002088 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002089 SkScalar fXRadius;
2090 SkScalar fYRadius;
2091 SkScalar fInnerXRadius;
2092 SkScalar fInnerYRadius;
2093 SkRect fDevBounds;
2094 };
joshualitt76e7fb62015-02-11 08:52:27 -08002095
Brian Salomon289e3d82016-12-14 15:52:56 -05002096 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002097 Helper fHelper;
2098 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002099 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002100 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002101 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002102
Chris Daltoneb694b72020-03-16 09:25:50 -06002103 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002104 GrProgramInfo* fProgramInfo = nullptr;
2105
John Stiles7571f9e2020-09-02 22:42:33 -04002106 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002107};
2108
joshualitt76e7fb62015-02-11 08:52:27 -08002109/////////////////////////////////////////////////////////////////////////////////////////////////
2110
Brian Salomon05441c42017-05-15 16:45:49 -04002111class DIEllipseOp : public GrMeshDrawOp {
2112private:
2113 using Helper = GrSimpleMeshDrawOpHelper;
2114
2115 struct DeviceSpaceParams {
2116 SkPoint fCenter;
2117 SkScalar fXRadius;
2118 SkScalar fYRadius;
2119 SkScalar fInnerXRadius;
2120 SkScalar fInnerYRadius;
2121 DIEllipseStyle fStyle;
2122 };
2123
joshualitt76e7fb62015-02-11 08:52:27 -08002124public:
Brian Salomon25a88092016-12-01 09:36:50 -05002125 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002126
Herb Derbyc76d4092020-10-07 16:46:15 -04002127 static GrOp::Owner Make(GrRecordingContext* context,
2128 GrPaint&& paint,
2129 const SkMatrix& viewMatrix,
2130 const SkRect& ellipse,
2131 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002132 DeviceSpaceParams params;
2133 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2134 params.fXRadius = SkScalarHalf(ellipse.width());
2135 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002136
bsalomon4b4a7cc2016-07-08 04:42:54 -07002137 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002138 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2139 ? DIEllipseStyle::kStroke
2140 : (SkStrokeRec::kHairline_Style == style)
2141 ? DIEllipseStyle::kHairline
2142 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002143
Brian Salomon05441c42017-05-15 16:45:49 -04002144 params.fInnerXRadius = 0;
2145 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002146 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2147 SkScalar strokeWidth = stroke.getWidth();
2148
2149 if (SkScalarNearlyZero(strokeWidth)) {
2150 strokeWidth = SK_ScalarHalf;
2151 } else {
2152 strokeWidth *= SK_ScalarHalf;
2153 }
2154
2155 // we only handle thick strokes for near-circular ellipses
2156 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002157 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2158 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002159 return nullptr;
2160 }
2161
2162 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002163 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2164 (strokeWidth * strokeWidth) * params.fXRadius) {
2165 return nullptr;
2166 }
2167 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2168 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002169 return nullptr;
2170 }
2171
2172 // set inner radius (if needed)
2173 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002174 params.fInnerXRadius = params.fXRadius - strokeWidth;
2175 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002176 }
2177
Brian Salomon05441c42017-05-15 16:45:49 -04002178 params.fXRadius += strokeWidth;
2179 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002180 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002181
2182 // For large ovals with low precision floats, we fall back to the path renderer.
2183 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2184 // minimum value to avoid divides by zero. With large ovals and low precision this
2185 // leads to blurring at the edge of the oval.
2186 const SkScalar kMaxOvalRadius = 16384;
2187 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2188 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2189 return nullptr;
2190 }
2191
Brian Salomon05441c42017-05-15 16:45:49 -04002192 if (DIEllipseStyle::kStroke == params.fStyle &&
2193 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2194 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002195 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002196 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002197 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002198
Herb Derbyc76d4092020-10-07 16:46:15 -04002199 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002200 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002201 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002202 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002203 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002204 // This expands the outer rect so that after CTM we end up with a half-pixel border
2205 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2206 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2207 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2208 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2209 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2210 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002211
Brian Salomon05441c42017-05-15 16:45:49 -04002212 fEllipses.emplace_back(
2213 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2214 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2215 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2216 params.fCenter.fY - params.fYRadius - geoDy,
2217 params.fCenter.fX + params.fXRadius + geoDx,
2218 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002219 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002220 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002221 }
2222
Brian Salomon289e3d82016-12-14 15:52:56 -05002223 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002224
Robert Phillips294723d2021-06-17 09:23:58 -04002225 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002226 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002227 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002228 } else {
2229 fHelper.visitProxies(func);
2230 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002231 }
2232
Chris Dalton57ab06c2021-04-22 12:57:28 -06002233 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2234 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002235 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2236 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002237 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002238 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002239 GrProcessorAnalysisCoverage::kSingleChannel, color,
2240 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002241 }
2242
2243 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2244
bsalomone46f9fe2015-08-18 06:05:14 -07002245private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002246 GrProgramInfo* programInfo() override { return fProgramInfo; }
2247
Robert Phillips4133dc42020-03-11 15:55:55 -04002248 void onCreateProgramInfo(const GrCaps* caps,
2249 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002250 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002251 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002252 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002253 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002254 GrXferBarrierFlags renderPassXferBarriers,
2255 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002256 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2257 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002258 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002259
Brian Salomon8afde5f2020-04-01 16:22:00 -04002260 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002261 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002262 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002263 }
2264
Robert Phillips71143952021-06-17 14:55:07 -04002265 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002266 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002267 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002268 }
2269
Robert Phillips787fd9d2021-03-22 14:48:09 -04002270 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002271 GrVertexWriter verts{helper.vertices()};
2272 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002273 return;
2274 }
2275
Brian Salomon05441c42017-05-15 16:45:49 -04002276 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002277 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002278 SkScalar xRadius = ellipse.fXRadius;
2279 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002280
joshualitt76e7fb62015-02-11 08:52:27 -08002281 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002282 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2283 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002284
Brian Osman9d958b52018-11-13 12:46:56 -05002285 // By default, constructed so that inner offset is (0, 0) for all points
2286 SkScalar innerRatioX = -offsetDx;
2287 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002288
Brian Osman9d958b52018-11-13 12:46:56 -05002289 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002290 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002291 innerRatioX = xRadius / ellipse.fInnerXRadius;
2292 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002293 }
joshualitt76e7fb62015-02-11 08:52:27 -08002294
Brian Osman2b6e3902018-11-21 15:29:43 -05002295 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2296 color,
2297 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002298 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002299 origin_centered_tri_strip(innerRatioX + offsetDx,
2300 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002301 }
Robert Phillips4490d922020-03-03 14:50:59 -05002302 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002303 }
2304
2305 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002306 if (!fProgramInfo || !fMesh) {
2307 return;
2308 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002309
Chris Dalton765ed362020-03-16 17:34:44 -06002310 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002311 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002312 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002313 }
halcanary9d524f22016-03-29 09:03:52 -07002314
Herb Derbye25c3002020-10-27 15:57:27 -04002315 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002316 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002317 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002318 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002319 }
2320
bsalomoncdaa97b2016-03-08 08:30:14 -08002321 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002322 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002323 }
2324
joshualittd96a67b2015-05-05 14:09:05 -07002325 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002326 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002327 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002328 }
2329
Brian Salomon05441c42017-05-15 16:45:49 -04002330 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002331 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002332 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002333 }
2334
John Stilesaf366522020-08-13 09:57:34 -04002335#if GR_TEST_UTILS
2336 SkString onDumpInfo() const override {
2337 SkString string;
2338 for (const auto& geo : fEllipses) {
2339 string.appendf(
2340 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2341 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2342 "GeoDY: %.2f\n",
2343 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2344 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2345 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2346 }
2347 string += fHelper.dumpInfo();
2348 return string;
2349 }
2350#endif
2351
Brian Salomon05441c42017-05-15 16:45:49 -04002352 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2353 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002354
Brian Salomon05441c42017-05-15 16:45:49 -04002355 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002356 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002357 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002358 SkScalar fXRadius;
2359 SkScalar fYRadius;
2360 SkScalar fInnerXRadius;
2361 SkScalar fInnerYRadius;
2362 SkScalar fGeoDx;
2363 SkScalar fGeoDy;
2364 DIEllipseStyle fStyle;
2365 SkRect fBounds;
2366 };
2367
Brian Salomon05441c42017-05-15 16:45:49 -04002368 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002369 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002370 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002371 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002372
Chris Daltoneb694b72020-03-16 09:25:50 -06002373 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002374 GrProgramInfo* fProgramInfo = nullptr;
2375
John Stiles7571f9e2020-09-02 22:42:33 -04002376 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002377};
2378
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002379///////////////////////////////////////////////////////////////////////////////
2380
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002381// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002382//
2383// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2384// ____________
2385// |_|________|_|
2386// | | | |
2387// | | | |
2388// | | | |
2389// |_|________|_|
2390// |_|________|_|
2391//
2392// For strokes, we don't draw the center quad.
2393//
2394// For circular roundrects, in the case where the stroke width is greater than twice
2395// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002396// in the center. The shared vertices are duplicated so we can set a different outer radius
2397// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002398// ____________
2399// |_|________|_|
2400// | |\ ____ /| |
2401// | | | | | |
2402// | | |____| | |
2403// |_|/______\|_|
2404// |_|________|_|
2405//
2406// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002407//
2408// For filled rrects that need to provide a distance vector we resuse the overstroke
2409// geometry but make the inner rect degenerate (either a point or a horizontal or
2410// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002411
jvanverth84839f62016-08-29 10:16:40 -07002412static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002413 // clang-format off
2414 // overstroke quads
2415 // we place this at the beginning so that we can skip these indices when rendering normally
2416 16, 17, 19, 16, 19, 18,
2417 19, 17, 23, 19, 23, 21,
2418 21, 23, 22, 21, 22, 20,
2419 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002420
Brian Salomon289e3d82016-12-14 15:52:56 -05002421 // corners
2422 0, 1, 5, 0, 5, 4,
2423 2, 3, 7, 2, 7, 6,
2424 8, 9, 13, 8, 13, 12,
2425 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002426
Brian Salomon289e3d82016-12-14 15:52:56 -05002427 // edges
2428 1, 2, 6, 1, 6, 5,
2429 4, 5, 9, 4, 9, 8,
2430 6, 7, 11, 6, 11, 10,
2431 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002432
Brian Salomon289e3d82016-12-14 15:52:56 -05002433 // center
2434 // we place this at the end so that we can ignore these indices when not rendering as filled
2435 5, 6, 10, 5, 10, 9,
2436 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002437};
Brian Salomon289e3d82016-12-14 15:52:56 -05002438
jvanverth84839f62016-08-29 10:16:40 -07002439// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002440static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002441
jvanverth84839f62016-08-29 10:16:40 -07002442// overstroke count is arraysize minus the center indices
2443static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2444// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002445static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002446// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002447static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2448static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002449static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002450
jvanverthc3d0e422016-08-25 08:12:35 -07002451enum RRectType {
2452 kFill_RRectType,
2453 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002454 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002455};
2456
jvanverth84839f62016-08-29 10:16:40 -07002457static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002458 switch (type) {
2459 case kFill_RRectType:
2460 case kStroke_RRectType:
2461 return kVertsPerStandardRRect;
2462 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002463 return kVertsPerOverstrokeRRect;
2464 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002465 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002466}
2467
2468static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002469 switch (type) {
2470 case kFill_RRectType:
2471 return kIndicesPerFillRRect;
2472 case kStroke_RRectType:
2473 return kIndicesPerStrokeRRect;
2474 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002475 return kIndicesPerOverstrokeRRect;
2476 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002477 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002478}
2479
2480static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002481 switch (type) {
2482 case kFill_RRectType:
2483 case kStroke_RRectType:
2484 return gStandardRRectIndices;
2485 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002486 return gOverstrokeRRectIndices;
2487 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002488 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002489}
2490
joshualitt76e7fb62015-02-11 08:52:27 -08002491///////////////////////////////////////////////////////////////////////////////////////////////////
2492
Robert Phillips79839d42016-10-06 15:03:34 -04002493// For distance computations in the interior of filled rrects we:
2494//
2495// add a interior degenerate (point or line) rect
2496// each vertex of that rect gets -outerRad as its radius
2497// this makes the computation of the distance to the outer edge be negative
2498// negative values are caught and then handled differently in the GP's onEmitCode
2499// each vertex is also given the normalized x & y distance from the interior rect's edge
2500// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2501
Brian Salomon05441c42017-05-15 16:45:49 -04002502class CircularRRectOp : public GrMeshDrawOp {
2503private:
2504 using Helper = GrSimpleMeshDrawOpHelper;
2505
joshualitt76e7fb62015-02-11 08:52:27 -08002506public:
Brian Salomon25a88092016-12-01 09:36:50 -05002507 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002508
bsalomon4b4a7cc2016-07-08 04:42:54 -07002509 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2510 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002511 static GrOp::Owner Make(GrRecordingContext* context,
2512 GrPaint&& paint,
2513 const SkMatrix& viewMatrix,
2514 const SkRect& devRect,
2515 float devRadius,
2516 float devStrokeWidth,
2517 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002518 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002519 devRect, devRadius,
2520 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002521 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002522 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002523 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2524 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002525 : INHERITED(ClassID())
2526 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002527 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002528 SkRect bounds = devRect;
2529 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2530 SkScalar innerRadius = 0.0f;
2531 SkScalar outerRadius = devRadius;
2532 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002533 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002534 if (devStrokeWidth > 0) {
2535 if (SkScalarNearlyZero(devStrokeWidth)) {
2536 halfWidth = SK_ScalarHalf;
2537 } else {
2538 halfWidth = SkScalarHalf(devStrokeWidth);
2539 }
joshualitt76e7fb62015-02-11 08:52:27 -08002540
bsalomon4b4a7cc2016-07-08 04:42:54 -07002541 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002542 // Outset stroke by 1/4 pixel
2543 devStrokeWidth += 0.25f;
2544 // If stroke is greater than width or height, this is still a fill
2545 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002546 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002547 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002548 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002549 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002550 }
2551 outerRadius += halfWidth;
2552 bounds.outset(halfWidth, halfWidth);
2553 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002554
Greg Daniel2655ede2019-04-10 00:49:28 +00002555 // The radii are outset for two reasons. First, it allows the shader to simply perform
2556 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2557 // Second, the outer radius is used to compute the verts of the bounding box that is
2558 // rendered and the outset ensures the box will cover all partially covered by the rrect
2559 // corners.
2560 outerRadius += SK_ScalarHalf;
2561 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002562
Greg Daniel5faf4742019-10-01 15:14:44 -04002563 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002564
Greg Daniel2655ede2019-04-10 00:49:28 +00002565 // Expand the rect for aa to generate correct vertices.
2566 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002567
Brian Salomon05441c42017-05-15 16:45:49 -04002568 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002569 fVertCount = rrect_type_to_vert_count(type);
2570 fIndexCount = rrect_type_to_index_count(type);
2571 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002572 }
2573
Brian Salomon289e3d82016-12-14 15:52:56 -05002574 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002575
Robert Phillips294723d2021-06-17 09:23:58 -04002576 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002577 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002578 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002579 } else {
2580 fHelper.visitProxies(func);
2581 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002582 }
2583
Chris Dalton57ab06c2021-04-22 12:57:28 -06002584 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2585 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002586 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002587 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002588 GrProcessorAnalysisCoverage::kSingleChannel, color,
2589 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002590 }
2591
2592 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2593
Brian Salomon92aee3d2016-12-21 09:20:25 -05002594private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002595 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002596 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002597 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002598 SkASSERT(smInset < bigInset);
2599
2600 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002601 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2602 color,
2603 xOffset, 0.0f,
2604 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002605
2606 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002607 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2608 color,
2609 xOffset, 0.0f,
2610 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002611
Brian Osmana1d4eb92018-12-06 16:33:10 -05002612 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2613 color,
2614 0.0f, 0.0f,
2615 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002616
Brian Osmana1d4eb92018-12-06 16:33:10 -05002617 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2618 color,
2619 0.0f, 0.0f,
2620 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002621
Brian Osmana1d4eb92018-12-06 16:33:10 -05002622 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2623 color,
2624 0.0f, 0.0f,
2625 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002626
Brian Osmana1d4eb92018-12-06 16:33:10 -05002627 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2628 color,
2629 0.0f, 0.0f,
2630 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002631
2632 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002633 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2634 color,
2635 xOffset, 0.0f,
2636 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002637
2638 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002639 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2640 color,
2641 xOffset, 0.0f,
2642 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002643 }
2644
Robert Phillips2669a7b2020-03-12 12:07:19 -04002645 GrProgramInfo* programInfo() override { return fProgramInfo; }
2646
Robert Phillips4133dc42020-03-11 15:55:55 -04002647 void onCreateProgramInfo(const GrCaps* caps,
2648 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002649 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002650 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002651 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002652 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002653 GrXferBarrierFlags renderPassXferBarriers,
2654 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002655 // Invert the view matrix as a local matrix (if any other processors require coords).
2656 SkMatrix localMatrix;
2657 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002658 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002659 }
2660
Robert Phillips4490d922020-03-03 14:50:59 -05002661 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002662 false, false, false, false,
2663 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002664
Brian Salomon8afde5f2020-04-01 16:22:00 -04002665 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002666 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002667 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002668 }
2669
Robert Phillips71143952021-06-17 14:55:07 -04002670 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002671 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002672 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002673 if (!fProgramInfo) {
2674 return;
2675 }
2676 }
2677
Brian Salomon12d22642019-01-29 14:38:50 -05002678 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002679 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002680
Robert Phillips787fd9d2021-03-22 14:48:09 -04002681 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002682 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002683 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002684 SkDebugf("Could not allocate vertices\n");
2685 return;
2686 }
2687
Brian Salomon12d22642019-01-29 14:38:50 -05002688 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002689 int firstIndex = 0;
2690 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2691 if (!indices) {
2692 SkDebugf("Could not allocate indices\n");
2693 return;
2694 }
2695
2696 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002697 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002698 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002699 SkScalar outerRadius = rrect.fOuterRadius;
2700 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002701
Brian Salomon289e3d82016-12-14 15:52:56 -05002702 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2703 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002704
Brian Salomon289e3d82016-12-14 15:52:56 -05002705 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002706 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002707 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002708 SkScalar innerRadius = rrect.fType != kFill_RRectType
2709 ? rrect.fInnerRadius / rrect.fOuterRadius
2710 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002711 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002712 verts.write(bounds.fLeft, yCoords[i],
2713 color,
2714 -1.0f, yOuterRadii[i],
2715 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002716
Brian Osmana1d4eb92018-12-06 16:33:10 -05002717 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2718 color,
2719 0.0f, yOuterRadii[i],
2720 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002721
Brian Osmana1d4eb92018-12-06 16:33:10 -05002722 verts.write(bounds.fRight - outerRadius, yCoords[i],
2723 color,
2724 0.0f, yOuterRadii[i],
2725 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002726
Brian Osmana1d4eb92018-12-06 16:33:10 -05002727 verts.write(bounds.fRight, yCoords[i],
2728 color,
2729 1.0f, yOuterRadii[i],
2730 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002731 }
jvanverthc3d0e422016-08-25 08:12:35 -07002732 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002733 // Effectively this is an additional stroked rrect, with its
2734 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2735 // This will give us correct AA in the center and the correct
2736 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002737 //
jvanvertha4f1af82016-08-29 07:17:47 -07002738 // Also, the outer offset is a constant vector pointing to the right, which
2739 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002740 if (kOverstroke_RRectType == rrect.fType) {
2741 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002742
Brian Salomon05441c42017-05-15 16:45:49 -04002743 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002744 // this is the normalized distance from the outer rectangle of this
2745 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002746 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002747
Brian Osmana1d4eb92018-12-06 16:33:10 -05002748 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002749 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002750 }
jvanverth6a397612016-08-26 08:15:33 -07002751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2753 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002754 for (int i = 0; i < primIndexCount; ++i) {
2755 *indices++ = primIndices[i] + currStartVertex;
2756 }
2757
Brian Salomon05441c42017-05-15 16:45:49 -04002758 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002759 }
2760
Robert Phillips4490d922020-03-03 14:50:59 -05002761 fMesh = target->allocMesh();
2762 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002763 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002764 }
2765
2766 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002767 if (!fProgramInfo || !fMesh) {
2768 return;
2769 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002770
Chris Dalton765ed362020-03-16 17:34:44 -06002771 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002772 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002773 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002774 }
2775
Herb Derbye25c3002020-10-27 15:57:27 -04002776 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002777 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002778
2779 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002780 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002781 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002782 }
2783
Brian Salomon05441c42017-05-15 16:45:49 -04002784 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002785 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002786 }
2787
Brian Salomon05441c42017-05-15 16:45:49 -04002788 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002789 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2790 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002791 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002792 }
2793
Brian Salomon05441c42017-05-15 16:45:49 -04002794 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002795 fVertCount += that->fVertCount;
2796 fIndexCount += that->fIndexCount;
2797 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002798 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002799 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002800 }
2801
John Stilesaf366522020-08-13 09:57:34 -04002802#if GR_TEST_UTILS
2803 SkString onDumpInfo() const override {
2804 SkString string;
2805 for (int i = 0; i < fRRects.count(); ++i) {
2806 string.appendf(
2807 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2808 "InnerRad: %.2f, OuterRad: %.2f\n",
2809 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2810 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2811 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2812 fRRects[i].fOuterRadius);
2813 }
2814 string += fHelper.dumpInfo();
2815 return string;
2816 }
2817#endif
2818
Brian Salomon05441c42017-05-15 16:45:49 -04002819 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002820 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002821 SkScalar fInnerRadius;
2822 SkScalar fOuterRadius;
2823 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002824 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002825 };
2826
Brian Salomon289e3d82016-12-14 15:52:56 -05002827 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002828 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002829 int fVertCount;
2830 int fIndexCount;
2831 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002832 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002833 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002834
Chris Daltoneb694b72020-03-16 09:25:50 -06002835 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002836 GrProgramInfo* fProgramInfo = nullptr;
2837
John Stiles7571f9e2020-09-02 22:42:33 -04002838 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002839};
2840
jvanverth84839f62016-08-29 10:16:40 -07002841static const int kNumRRectsInIndexBuffer = 256;
2842
2843GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2844GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002845static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2846 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002847 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2848 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2849 switch (type) {
2850 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002851 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002852 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2853 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002854 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002855 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002856 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2857 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002858 default:
2859 SkASSERT(false);
2860 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002861 }
jvanverth84839f62016-08-29 10:16:40 -07002862}
2863
Brian Salomon05441c42017-05-15 16:45:49 -04002864class EllipticalRRectOp : public GrMeshDrawOp {
2865private:
2866 using Helper = GrSimpleMeshDrawOpHelper;
2867
joshualitt76e7fb62015-02-11 08:52:27 -08002868public:
Brian Salomon25a88092016-12-01 09:36:50 -05002869 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002870
bsalomon4b4a7cc2016-07-08 04:42:54 -07002871 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2872 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002873 static GrOp::Owner Make(GrRecordingContext* context,
2874 GrPaint&& paint,
2875 const SkMatrix& viewMatrix,
2876 const SkRect& devRect,
2877 float devXRadius,
2878 float devYRadius,
2879 SkVector devStrokeWidths,
2880 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002881 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2882 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002883 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2884 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002885 if (devStrokeWidths.fX > 0) {
2886 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2887 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2888 } else {
2889 devStrokeWidths.scale(SK_ScalarHalf);
2890 }
joshualitt76e7fb62015-02-11 08:52:27 -08002891
bsalomon4b4a7cc2016-07-08 04:42:54 -07002892 // we only handle thick strokes for near-circular ellipses
2893 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002894 (SK_ScalarHalf * devXRadius > devYRadius ||
2895 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002896 return nullptr;
2897 }
2898
2899 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002900 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2901 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002902 return nullptr;
2903 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002904 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2905 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002906 return nullptr;
2907 }
Brian Salomon05441c42017-05-15 16:45:49 -04002908 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002909 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002910 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002911 devXRadius, devYRadius, devStrokeWidths,
2912 strokeOnly);
2913 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002914
Herb Derbyc76d4092020-10-07 16:46:15 -04002915 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002916 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2917 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002918 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002919 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002920 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002921 SkScalar innerXRadius = 0.0f;
2922 SkScalar innerYRadius = 0.0f;
2923 SkRect bounds = devRect;
2924 bool stroked = false;
2925 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002926 // this is legit only if scale & translation (which should be the case at the moment)
2927 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002928 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2929 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002930 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2931 }
2932
Brian Salomon05441c42017-05-15 16:45:49 -04002933 devXRadius += devStrokeHalfWidths.fX;
2934 devYRadius += devStrokeHalfWidths.fY;
2935 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002936 }
2937
Brian Salomon05441c42017-05-15 16:45:49 -04002938 fStroked = stroked;
2939 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002940 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002941 // Expand the rect for aa in order to generate the correct vertices.
2942 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002943 fRRects.emplace_back(
2944 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002945 }
2946
Brian Salomon289e3d82016-12-14 15:52:56 -05002947 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002948
Robert Phillips294723d2021-06-17 09:23:58 -04002949 void visitProxies(const GrVisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002950 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002951 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002952 } else {
2953 fHelper.visitProxies(func);
2954 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002955 }
2956
Chris Dalton57ab06c2021-04-22 12:57:28 -06002957 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2958 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002959 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002960 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002961 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002962 GrProcessorAnalysisCoverage::kSingleChannel, color,
2963 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002964 }
2965
2966 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2967
bsalomone46f9fe2015-08-18 06:05:14 -07002968private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002969 GrProgramInfo* programInfo() override { return fProgramInfo; }
2970
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 void onCreateProgramInfo(const GrCaps* caps,
2972 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002973 const GrSurfaceProxyView& writeView,
Chris Dalton6aaf00f2021-07-13 13:26:39 -06002974 bool usesMSAASurface,
Robert Phillips4133dc42020-03-11 15:55:55 -04002975 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002976 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002977 GrXferBarrierFlags renderPassXferBarriers,
2978 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002979 SkMatrix localMatrix;
2980 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002981 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002982 }
2983
Robert Phillips4490d922020-03-03 14:50:59 -05002984 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2985 fUseScale, localMatrix);
2986
Brian Salomon8afde5f2020-04-01 16:22:00 -04002987 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002988 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002989 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002990 }
2991
Robert Phillips71143952021-06-17 14:55:07 -04002992 void onPrepareDraws(GrMeshDrawTarget* target) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002993 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002994 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002995 if (!fProgramInfo) {
2996 return;
2997 }
2998 }
joshualitt76e7fb62015-02-11 08:52:27 -08002999
bsalomonb5238a72015-05-05 07:49:49 -07003000 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07003001 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04003002 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
3003 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003004
Brian Salomon12d22642019-01-29 14:38:50 -05003005 if (!indexBuffer) {
3006 SkDebugf("Could not allocate indices\n");
3007 return;
3008 }
Robert Phillips4490d922020-03-03 14:50:59 -05003009 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003010 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003011 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003012 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003013 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003014 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003015 SkDebugf("Could not allocate vertices\n");
3016 return;
3017 }
3018
Brian Salomon05441c42017-05-15 16:45:49 -04003019 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003020 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003021 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003022 float reciprocalRadii[4] = {
3023 SkScalarInvert(rrect.fXRadius),
3024 SkScalarInvert(rrect.fYRadius),
3025 SkScalarInvert(rrect.fInnerXRadius),
3026 SkScalarInvert(rrect.fInnerYRadius)
3027 };
joshualitt76e7fb62015-02-11 08:52:27 -08003028
Brian Osmane3afdd52020-10-28 10:49:56 -04003029 // If the stroke width is exactly double the radius, the inner radii will be zero.
3030 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3031 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3032 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3033
joshualitt76e7fb62015-02-11 08:52:27 -08003034 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003035 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3036 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003037
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003038 SkScalar xMaxOffset = xOuterRadius;
3039 SkScalar yMaxOffset = yOuterRadius;
3040 if (!fStroked) {
3041 // For filled rrects we map a unit circle in the vertex attributes rather than
3042 // computing an ellipse and modifying that distance, so we normalize to 1.
3043 xMaxOffset /= rrect.fXRadius;
3044 yMaxOffset /= rrect.fYRadius;
3045 }
3046
Brian Salomon05441c42017-05-15 16:45:49 -04003047 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003048
Brian Salomon289e3d82016-12-14 15:52:56 -05003049 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3050 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003051 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003052 SK_ScalarNearlyZero, // we're using inversesqrt() in
3053 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003054 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003055
Brian Osman788b9162020-02-07 10:36:46 -05003056 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003057 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 verts.write(bounds.fLeft, yCoords[i],
3059 color,
3060 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003061 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003062 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003063
Brian Osmana1d4eb92018-12-06 16:33:10 -05003064 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3065 color,
3066 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003067 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003068 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003069
Brian Osmana1d4eb92018-12-06 16:33:10 -05003070 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3071 color,
3072 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003073 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003074 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003075
Brian Osmana1d4eb92018-12-06 16:33:10 -05003076 verts.write(bounds.fRight, yCoords[i],
3077 color,
3078 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003079 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003080 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003081 }
3082 }
Robert Phillips4490d922020-03-03 14:50:59 -05003083 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003084 }
3085
3086 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003087 if (!fProgramInfo || !fMesh) {
3088 return;
3089 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003090
Chris Dalton765ed362020-03-16 17:34:44 -06003091 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003092 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003093 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003094 }
3095
Herb Derbye25c3002020-10-27 15:57:27 -04003096 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003097 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003098
Brian Salomon05441c42017-05-15 16:45:49 -04003099 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003100 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003101 }
3102
bsalomoncdaa97b2016-03-08 08:30:14 -08003103 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003104 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003105 }
3106
Brian Salomon05441c42017-05-15 16:45:49 -04003107 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003108 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3109 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003110 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003111 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003112
Brian Salomon05441c42017-05-15 16:45:49 -04003113 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003114 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003115 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003116 }
3117
John Stilesaf366522020-08-13 09:57:34 -04003118#if GR_TEST_UTILS
3119 SkString onDumpInfo() const override {
3120 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3121 for (const auto& geo : fRRects) {
3122 string.appendf(
3123 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3124 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3125 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3126 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3127 geo.fInnerXRadius, geo.fInnerYRadius);
3128 }
3129 string += fHelper.dumpInfo();
3130 return string;
3131 }
3132#endif
3133
Brian Salomon05441c42017-05-15 16:45:49 -04003134 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003135 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003136 SkScalar fXRadius;
3137 SkScalar fYRadius;
3138 SkScalar fInnerXRadius;
3139 SkScalar fInnerYRadius;
3140 SkRect fDevBounds;
3141 };
3142
Brian Salomon289e3d82016-12-14 15:52:56 -05003143 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003144 Helper fHelper;
3145 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003146 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003147 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003148 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003149
Chris Daltoneb694b72020-03-16 09:25:50 -06003150 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003151 GrProgramInfo* fProgramInfo = nullptr;
3152
John Stiles7571f9e2020-09-02 22:42:33 -04003153 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003154};
3155
Herb Derbyc76d4092020-10-07 16:46:15 -04003156GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3157 GrPaint&& paint,
3158 const SkMatrix& viewMatrix,
3159 const SkRRect& rrect,
3160 const SkStrokeRec& stroke,
3161 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003162 SkASSERT(viewMatrix.rectStaysRect());
3163 SkASSERT(viewMatrix.isSimilarity());
3164 SkASSERT(rrect.isSimple());
3165 SkASSERT(!rrect.isOval());
3166 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3167
3168 // RRect ops only handle simple, but not too simple, rrects.
3169 // Do any matrix crunching before we reset the draw state for device coords.
3170 const SkRect& rrectBounds = rrect.getBounds();
3171 SkRect bounds;
3172 viewMatrix.mapRect(&bounds, rrectBounds);
3173
3174 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3175 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3176 viewMatrix[SkMatrix::kMSkewY]));
3177
3178 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3179 SkScalar scaledStroke = -1;
3180 SkScalar strokeWidth = stroke.getWidth();
3181 SkStrokeRec::Style style = stroke.getStyle();
3182
3183 bool isStrokeOnly =
3184 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3185 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3186
3187 if (hasStroke) {
3188 if (SkStrokeRec::kHairline_Style == style) {
3189 scaledStroke = SK_Scalar1;
3190 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003191 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3192 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003193 }
3194 }
3195
3196 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3197 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3198 // patch will have fractional coverage. This only matters when the interior is actually filled.
3199 // We could consider falling back to rect rendering here, since a tiny radius is
3200 // indistinguishable from a square corner.
3201 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3202 return nullptr;
3203 }
3204
3205 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3206 scaledStroke, isStrokeOnly);
3207}
3208
Herb Derbyc76d4092020-10-07 16:46:15 -04003209GrOp::Owner make_rrect_op(GrRecordingContext* context,
3210 GrPaint&& paint,
3211 const SkMatrix& viewMatrix,
3212 const SkRRect& rrect,
3213 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003214 SkASSERT(viewMatrix.rectStaysRect());
3215 SkASSERT(rrect.isSimple());
3216 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003217
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003218 // RRect ops only handle simple, but not too simple, rrects.
3219 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003220 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003221 SkRect bounds;
3222 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003223
Mike Reed242135a2018-02-22 13:41:39 -05003224 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003225 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3226 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3227 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3228 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003229
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003230 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003231
bsalomon4b4a7cc2016-07-08 04:42:54 -07003232 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3233 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003234 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235
Brian Salomon289e3d82016-12-14 15:52:56 -05003236 bool isStrokeOnly =
3237 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003238 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3239
3240 if (hasStroke) {
3241 if (SkStrokeRec::kHairline_Style == style) {
3242 scaledStroke.set(1, 1);
3243 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003244 scaledStroke.fX = SkScalarAbs(
3245 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3246 scaledStroke.fY = SkScalarAbs(
3247 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003248 }
3249
Jim Van Verth64b85892019-06-17 12:01:46 -04003250 // if half of strokewidth is greater than radius, we don't handle that right now
3251 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3252 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003253 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003254 }
3255 }
3256
Brian Salomon8a97f562019-04-18 14:07:27 -04003257 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003258 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003259 std::swap(xRadius, yRadius);
3260 std::swap(scaledStroke.fX, scaledStroke.fY);
3261 }
3262
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003263 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3264 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3265 // patch will have fractional coverage. This only matters when the interior is actually filled.
3266 // We could consider falling back to rect rendering here, since a tiny radius is
3267 // indistinguishable from a square corner.
3268 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003269 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003270 }
3271
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003272 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003273 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3274 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003275}
3276
Herb Derbyc76d4092020-10-07 16:46:15 -04003277GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3278 GrPaint&& paint,
3279 const SkMatrix& viewMatrix,
3280 const SkRRect& rrect,
3281 const SkStrokeRec& stroke,
3282 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003283 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003284 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003285 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003286 }
3287
3288 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003289 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003290 }
3291
Greg Daniel2655ede2019-04-10 00:49:28 +00003292 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003293}
joshualitt3e708c52015-04-30 13:49:27 -07003294
bsalomon4b4a7cc2016-07-08 04:42:54 -07003295///////////////////////////////////////////////////////////////////////////////
3296
Herb Derbyc76d4092020-10-07 16:46:15 -04003297GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3298 GrPaint&& paint,
3299 const SkMatrix& viewMatrix,
3300 const SkRect& oval,
3301 const GrStyle& style,
3302 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003303 SkScalar width = oval.width();
3304 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3305 circle_stays_circle(viewMatrix));
3306
3307 auto r = width / 2.f;
3308 SkPoint center = { oval.centerX(), oval.centerY() };
3309 if (style.hasNonDashPathEffect()) {
3310 return nullptr;
3311 } else if (style.isDashed()) {
3312 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3313 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3314 return nullptr;
3315 }
3316 auto onInterval = style.dashIntervals()[0];
3317 auto offInterval = style.dashIntervals()[1];
3318 if (offInterval == 0) {
3319 GrStyle strokeStyle(style.strokeRec(), nullptr);
3320 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3321 strokeStyle, shaderCaps);
3322 } else if (onInterval == 0) {
3323 // There is nothing to draw but we have no way to indicate that here.
3324 return nullptr;
3325 }
3326 auto angularOnInterval = onInterval / r;
3327 auto angularOffInterval = offInterval / r;
3328 auto phaseAngle = style.dashPhase() / r;
3329 // Currently this function doesn't accept ovals with different start angles, though
3330 // it could.
3331 static const SkScalar kStartAngle = 0.f;
3332 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3333 style.strokeRec().getWidth(), kStartAngle,
3334 angularOnInterval, angularOffInterval, phaseAngle);
3335 }
3336 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3337}
3338
Herb Derbyc76d4092020-10-07 16:46:15 -04003339GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3340 GrPaint&& paint,
3341 const SkMatrix& viewMatrix,
3342 const SkRect& oval,
3343 const GrStyle& style,
3344 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003345 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003346 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003347 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3348 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003349 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003350 }
3351
3352 if (style.pathEffect()) {
3353 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003354 }
3355
Stan Ilieveb868aa2017-02-21 11:06:16 -05003356 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003357 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003358 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003359 }
3360
Stan Ilieveb868aa2017-02-21 11:06:16 -05003361 // Otherwise, if we have shader derivative support, render as device-independent
3362 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003363 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3364 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3365 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3366 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3367 // Check for near-degenerate matrix
3368 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003369 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003370 style.strokeRec());
3371 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003372 }
3373
bsalomon4b4a7cc2016-07-08 04:42:54 -07003374 return nullptr;
3375}
3376
3377///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003378
Herb Derbyc76d4092020-10-07 16:46:15 -04003379GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3380 GrPaint&& paint,
3381 const SkMatrix& viewMatrix,
3382 const SkRect& oval, SkScalar startAngle,
3383 SkScalar sweepAngle, bool useCenter,
3384 const GrStyle& style,
3385 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003386 SkASSERT(!oval.isEmpty());
3387 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003388 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003389 if (SkScalarAbs(sweepAngle) >= 360.f) {
3390 return nullptr;
3391 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3393 return nullptr;
3394 }
3395 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003396 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3397 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003398 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003399 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003400}
3401
3402///////////////////////////////////////////////////////////////////////////////
3403
Hal Canary6f6961e2017-01-31 13:50:44 -05003404#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003405
Brian Salomon05441c42017-05-15 16:45:49 -04003406GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003407 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003408 SkScalar rotate = random->nextSScalar1() * 360.f;
3409 SkScalar translateX = random->nextSScalar1() * 1000.f;
3410 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003411 SkScalar scale;
3412 do {
3413 scale = random->nextSScalar1() * 100.f;
3414 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003415 SkMatrix viewMatrix;
3416 viewMatrix.setRotate(rotate);
3417 viewMatrix.postTranslate(translateX, translateY);
3418 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003419 SkRect circle = GrTest::TestSquare(random);
3420 SkPoint center = {circle.centerX(), circle.centerY()};
3421 SkScalar radius = circle.width() / 2.f;
3422 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003423 CircleOp::ArcParams arcParamsTmp;
3424 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003425 if (random->nextBool()) {
3426 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003427 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3428 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003429 arcParams = &arcParamsTmp;
3430 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003431 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3432 center, radius,
3433 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003434 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003435 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003436 }
Mike Klein16885072018-12-11 09:54:31 -05003437 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003438 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003439}
3440
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003441GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3442 SkScalar rotate = random->nextSScalar1() * 360.f;
3443 SkScalar translateX = random->nextSScalar1() * 1000.f;
3444 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003445 SkScalar scale;
3446 do {
3447 scale = random->nextSScalar1() * 100.f;
3448 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003449 SkMatrix viewMatrix;
3450 viewMatrix.setRotate(rotate);
3451 viewMatrix.postTranslate(translateX, translateY);
3452 viewMatrix.postScale(scale, scale);
3453 SkRect circle = GrTest::TestSquare(random);
3454 SkPoint center = {circle.centerX(), circle.centerY()};
3455 SkScalar radius = circle.width() / 2.f;
3456 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3457 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3458 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3459 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3460 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003461 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3462 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003463 startAngle, onAngle, offAngle, phase);
3464}
3465
Brian Salomon05441c42017-05-15 16:45:49 -04003466GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003467 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003468 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003469 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003470 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003471}
3472
Brian Salomon05441c42017-05-15 16:45:49 -04003473GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003474 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003475 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003476 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003477 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003478}
3479
Jim Van Verth64b85892019-06-17 12:01:46 -04003480GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3481 do {
3482 SkScalar rotate = random->nextSScalar1() * 360.f;
3483 SkScalar translateX = random->nextSScalar1() * 1000.f;
3484 SkScalar translateY = random->nextSScalar1() * 1000.f;
3485 SkScalar scale;
3486 do {
3487 scale = random->nextSScalar1() * 100.f;
3488 } while (scale == 0);
3489 SkMatrix viewMatrix;
3490 viewMatrix.setRotate(rotate);
3491 viewMatrix.postTranslate(translateX, translateY);
3492 viewMatrix.postScale(scale, scale);
3493 SkRect rect = GrTest::TestRect(random);
3494 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3495 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3496 if (rrect.isOval()) {
3497 continue;
3498 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003499 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003500 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3501 GrTest::TestStrokeRec(random), nullptr);
3502 if (op) {
3503 return op;
3504 }
3505 assert_alive(paint);
3506 } while (true);
3507}
3508
Brian Salomon05441c42017-05-15 16:45:49 -04003509GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003510 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003511 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003512 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003513 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003514}
3515
3516#endif