blob: bc6b6030d03880bcf87a0496861a3bd6ebf3b5f7 [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
Chris Dalton1706cbf2019-05-21 19:35:29 -06001253 void visitProxies(const VisitProxyFunc& 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,
Robert Phillips4133dc42020-03-11 15:55:55 -04001277 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001278 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001279 GrXferBarrierFlags renderPassXferBarriers,
1280 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001281 SkMatrix localMatrix;
1282 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001283 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001284 }
1285
Robert Phillips4490d922020-03-03 14:50:59 -05001286 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001287 fClipPlaneIsect, fClipPlaneUnion,
1288 fRoundCaps, fWideColor,
1289 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001290
Brian Salomon8afde5f2020-04-01 16:22:00 -04001291 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001292 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001293 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001294 }
1295
Robert Phillips4490d922020-03-03 14:50:59 -05001296 void onPrepareDraws(Target* target) override {
1297 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001298 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001299 if (!fProgramInfo) {
1300 return;
1301 }
1302 }
1303
Brian Salomon12d22642019-01-29 14:38:50 -05001304 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001305 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001306 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001307 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001308 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001309 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001310 return;
1311 }
1312
Brian Salomon12d22642019-01-29 14:38:50 -05001313 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001314 int firstIndex = 0;
1315 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1316 if (!indices) {
1317 SkDebugf("Could not allocate indices\n");
1318 return;
1319 }
1320
1321 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001322 for (const auto& circle : fCircles) {
1323 SkScalar innerRadius = circle.fInnerRadius;
1324 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001325 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001326 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001327
joshualitt76e7fb62015-02-11 08:52:27 -08001328 // The inner radius in the vertex data must be specified in normalized space.
1329 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001330 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001331
1332 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001333 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001334
Brian Osman9a24fee2018-08-03 09:48:42 -04001335 SkVector geoClipPlane = { 0, 0 };
1336 SkScalar offsetClipDist = SK_Scalar1;
1337 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1338 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1339 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1340 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1341 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1342 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1343 // the AA can extend just past the center of the circle.
1344 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1345 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1346 SkAssertResult(geoClipPlane.normalize());
1347 offsetClipDist = 0.5f / halfWidth;
1348 }
1349
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 for (int i = 0; i < 8; ++i) {
1351 // This clips the normalized offset to the half-plane we computed above. Then we
1352 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001353 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001354 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(center + offset * halfWidth,
1356 color,
1357 offset,
1358 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001360 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001361 }
1362 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001364 }
1365 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001366 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001367 }
1368 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001370 }
Brian Salomon45c92202018-04-10 10:53:58 -04001371 }
jvanverth6ca48822016-10-07 06:57:32 -07001372
Brian Salomon05441c42017-05-15 16:45:49 -04001373 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001374 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001375
Brian Osman7d8f82b2018-11-08 10:24:09 -05001376 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001377 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1378 color,
1379 kOctagonInner[i] * innerRadius,
1380 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001381 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001382 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001383 }
1384 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001385 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001386 }
1387 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001388 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001389 }
1390 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001391 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001392 }
Brian Salomon45c92202018-04-10 10:53:58 -04001393 }
jvanverth6ca48822016-10-07 06:57:32 -07001394 } else {
1395 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001396 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001397 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001398 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001399 }
jvanverth6ca48822016-10-07 06:57:32 -07001400 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001401 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001402 }
1403 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001404 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001405 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001406 if (fRoundCaps) {
1407 vertices.write(circle.fRoundCapCenters);
1408 }
jvanverth6ca48822016-10-07 06:57:32 -07001409 }
1410
Brian Salomon05441c42017-05-15 16:45:49 -04001411 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1412 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001413 for (int i = 0; i < primIndexCount; ++i) {
1414 *indices++ = primIndices[i] + currStartVertex;
1415 }
1416
Brian Salomon05441c42017-05-15 16:45:49 -04001417 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001418 }
jvanverth6ca48822016-10-07 06:57:32 -07001419
Robert Phillips4490d922020-03-03 14:50:59 -05001420 fMesh = target->allocMesh();
1421 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001422 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001423 }
1424
1425 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001426 if (!fProgramInfo || !fMesh) {
1427 return;
1428 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001429
Chris Dalton765ed362020-03-16 17:34:44 -06001430 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001431 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001432 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001433 }
1434
Herb Derbye25c3002020-10-27 15:57:27 -04001435 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001436 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001437
1438 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001439 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001440 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001441 }
1442
Brian Salomon05441c42017-05-15 16:45:49 -04001443 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001444 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001445 }
1446
Brian Salomon05441c42017-05-15 16:45:49 -04001447 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001448 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1449 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001450 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001451 }
1452
Brian Salomon289e3d82016-12-14 15:52:56 -05001453 // Because we've set up the ops that don't use the planes with noop values
1454 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001455 fClipPlane |= that->fClipPlane;
1456 fClipPlaneIsect |= that->fClipPlaneIsect;
1457 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001458 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001459 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001460
Brian Salomon05441c42017-05-15 16:45:49 -04001461 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001462 fVertCount += that->fVertCount;
1463 fIndexCount += that->fIndexCount;
1464 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001465 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001466 }
1467
John Stilesaf366522020-08-13 09:57:34 -04001468#if GR_TEST_UTILS
1469 SkString onDumpInfo() const override {
1470 SkString string;
1471 for (int i = 0; i < fCircles.count(); ++i) {
1472 string.appendf(
1473 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1474 "InnerRad: %.2f, OuterRad: %.2f\n",
1475 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1476 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1477 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1478 fCircles[i].fOuterRadius);
1479 }
1480 string += fHelper.dumpInfo();
1481 return string;
1482 }
1483#endif
1484
Brian Salomon05441c42017-05-15 16:45:49 -04001485 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001486 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001487 SkScalar fInnerRadius;
1488 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001489 SkScalar fClipPlane[3];
1490 SkScalar fIsectPlane[3];
1491 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001492 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001493 SkRect fDevBounds;
1494 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001495 };
1496
Brian Salomon289e3d82016-12-14 15:52:56 -05001497 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001498 Helper fHelper;
1499 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001500 int fVertCount;
1501 int fIndexCount;
1502 bool fAllFill;
1503 bool fClipPlane;
1504 bool fClipPlaneIsect;
1505 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001506 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001507 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001508
Chris Daltoneb694b72020-03-16 09:25:50 -06001509 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001510 GrProgramInfo* fProgramInfo = nullptr;
1511
John Stiles7571f9e2020-09-02 22:42:33 -04001512 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001513};
1514
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001515class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1516private:
1517 using Helper = GrSimpleMeshDrawOpHelper;
1518
1519public:
1520 DEFINE_OP_CLASS_ID
1521
Herb Derbyc76d4092020-10-07 16:46:15 -04001522 static GrOp::Owner Make(GrRecordingContext* context,
1523 GrPaint&& paint,
1524 const SkMatrix& viewMatrix,
1525 SkPoint center,
1526 SkScalar radius,
1527 SkScalar strokeWidth,
1528 SkScalar startAngle,
1529 SkScalar onAngle,
1530 SkScalar offAngle,
1531 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001532 SkASSERT(circle_stays_circle(viewMatrix));
1533 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001534 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1535 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001536 onAngle, offAngle, phaseAngle);
1537 }
1538
Herb Derbyc76d4092020-10-07 16:46:15 -04001539 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001540 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1541 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1542 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001543 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001544 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001545 SkASSERT(circle_stays_circle(viewMatrix));
1546 viewMatrix.mapPoints(&center, 1);
1547 radius = viewMatrix.mapRadius(radius);
1548 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1549
1550 // Determine the angle where the circle starts in device space and whether its orientation
1551 // has been reversed.
1552 SkVector start;
1553 bool reflection;
1554 if (!startAngle) {
1555 start = {1, 0};
1556 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001557 start.fY = SkScalarSin(startAngle);
1558 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001559 }
1560 viewMatrix.mapVectors(&start, 1);
1561 startAngle = SkScalarATan2(start.fY, start.fX);
1562 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1563 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1564
1565 auto totalAngle = onAngle + offAngle;
1566 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1567
1568 SkScalar halfWidth = 0;
1569 if (SkScalarNearlyZero(strokeWidth)) {
1570 halfWidth = SK_ScalarHalf;
1571 } else {
1572 halfWidth = SkScalarHalf(strokeWidth);
1573 }
1574
1575 SkScalar outerRadius = radius + halfWidth;
1576 SkScalar innerRadius = radius - halfWidth;
1577
1578 // The radii are outset for two reasons. First, it allows the shader to simply perform
1579 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1580 // Second, the outer radius is used to compute the verts of the bounding box that is
1581 // rendered and the outset ensures the box will cover all partially covered by the circle.
1582 outerRadius += SK_ScalarHalf;
1583 innerRadius -= SK_ScalarHalf;
1584 fViewMatrixIfUsingLocalCoords = viewMatrix;
1585
1586 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1587 center.fX + outerRadius, center.fY + outerRadius);
1588
1589 // We store whether there is a reflection as a negative total angle.
1590 if (reflection) {
1591 totalAngle = -totalAngle;
1592 }
1593 fCircles.push_back(Circle{
1594 color,
1595 outerRadius,
1596 innerRadius,
1597 onAngle,
1598 totalAngle,
1599 startAngle,
1600 phaseAngle,
1601 devBounds
1602 });
1603 // Use the original radius and stroke radius for the bounds so that it does not include the
1604 // AA bloat.
1605 radius += halfWidth;
1606 this->setBounds(
1607 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001608 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001609 fVertCount = circle_type_to_vert_count(true);
1610 fIndexCount = circle_type_to_index_count(true);
1611 }
1612
1613 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1614
Chris Dalton1706cbf2019-05-21 19:35:29 -06001615 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001616 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001617 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001618 } else {
1619 fHelper.visitProxies(func);
1620 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001621 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001622
Chris Dalton57ab06c2021-04-22 12:57:28 -06001623 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1624 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001625 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001626 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001627 GrProcessorAnalysisCoverage::kSingleChannel, color,
1628 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001629 }
1630
1631 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1632
1633private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001634 GrProgramInfo* programInfo() override { return fProgramInfo; }
1635
Robert Phillips4133dc42020-03-11 15:55:55 -04001636 void onCreateProgramInfo(const GrCaps* caps,
1637 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001638 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001639 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001640 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001641 GrXferBarrierFlags renderPassXferBarriers,
1642 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001643 SkMatrix localMatrix;
1644 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001645 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001646 }
1647
1648 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001649 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001650 fWideColor,
1651 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001652
Brian Salomon8afde5f2020-04-01 16:22:00 -04001653 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001654 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001655 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001656 }
1657
Robert Phillips4490d922020-03-03 14:50:59 -05001658 void onPrepareDraws(Target* target) override {
1659 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001660 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001661 if (!fProgramInfo) {
1662 return;
1663 }
1664 }
1665
Brian Salomon12d22642019-01-29 14:38:50 -05001666 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001668 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001669 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001670 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001671 SkDebugf("Could not allocate vertices\n");
1672 return;
1673 }
1674
Brian Salomon12d22642019-01-29 14:38:50 -05001675 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676 int firstIndex = 0;
1677 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1678 if (!indices) {
1679 SkDebugf("Could not allocate indices\n");
1680 return;
1681 }
1682
1683 int currStartVertex = 0;
1684 for (const auto& circle : fCircles) {
1685 // The inner radius in the vertex data must be specified in normalized space so that
1686 // length() can be called with smaller values to avoid precision issues with half
1687 // floats.
1688 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1689 const SkRect& bounds = circle.fDevBounds;
1690 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001691 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1692 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1693 };
1694 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001695 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001696 dashParams.totalAngle = -dashParams.totalAngle;
1697 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001698 }
1699
Brian Osmane3caf2d2018-11-21 13:48:36 -05001700 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001701
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001702 // The bounding geometry for the circle is composed of an outer bounding octagon and
1703 // an inner bounded octagon.
1704
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001705 // Compute the vertices of the outer octagon.
1706 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1707 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001708
1709 auto reflectY = [=](const SkPoint& p) {
1710 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001711 };
Brian Osman9d958b52018-11-13 12:46:56 -05001712
1713 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001714 vertices.write(center + kOctagonOuter[i] * halfWidth,
1715 color,
1716 reflectY(kOctagonOuter[i]),
1717 circle.fOuterRadius,
1718 normInnerRadius,
1719 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001720 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721
1722 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001723 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001724 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1725 color,
1726 reflectY(kOctagonInner[i]) * normInnerRadius,
1727 circle.fOuterRadius,
1728 normInnerRadius,
1729 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001730 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001731
1732 const uint16_t* primIndices = circle_type_to_indices(true);
1733 const int primIndexCount = circle_type_to_index_count(true);
1734 for (int i = 0; i < primIndexCount; ++i) {
1735 *indices++ = primIndices[i] + currStartVertex;
1736 }
1737
1738 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001739 }
1740
Robert Phillips4490d922020-03-03 14:50:59 -05001741 fMesh = target->allocMesh();
1742 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001743 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001744 }
1745
1746 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001747 if (!fProgramInfo || !fMesh) {
1748 return;
1749 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001750
Chris Dalton765ed362020-03-16 17:34:44 -06001751 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001752 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001753 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001754 }
1755
Herb Derbye25c3002020-10-27 15:57:27 -04001756 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001757 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1758
1759 // can only represent 65535 unique vertices with 16-bit indices
1760 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001761 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001762 }
1763
1764 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001765 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001766 }
1767
1768 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001769 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1770 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001771 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001772 }
1773
1774 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001775 fVertCount += that->fVertCount;
1776 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001777 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001778 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001779 }
1780
John Stilesaf366522020-08-13 09:57:34 -04001781#if GR_TEST_UTILS
1782 SkString onDumpInfo() const override {
1783 SkString string;
1784 for (int i = 0; i < fCircles.count(); ++i) {
1785 string.appendf(
1786 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1787 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1788 "Phase: %.2f\n",
1789 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1790 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1791 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1792 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1793 fCircles[i].fPhaseAngle);
1794 }
1795 string += fHelper.dumpInfo();
1796 return string;
1797 }
1798#endif
1799
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001800 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001801 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001802 SkScalar fOuterRadius;
1803 SkScalar fInnerRadius;
1804 SkScalar fOnAngle;
1805 SkScalar fTotalAngle;
1806 SkScalar fStartAngle;
1807 SkScalar fPhaseAngle;
1808 SkRect fDevBounds;
1809 };
1810
1811 SkMatrix fViewMatrixIfUsingLocalCoords;
1812 Helper fHelper;
1813 SkSTArray<1, Circle, true> fCircles;
1814 int fVertCount;
1815 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001816 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001817
Chris Daltoneb694b72020-03-16 09:25:50 -06001818 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001819 GrProgramInfo* fProgramInfo = nullptr;
1820
John Stiles7571f9e2020-09-02 22:42:33 -04001821 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001822};
1823
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001824///////////////////////////////////////////////////////////////////////////////
1825
Brian Salomon05441c42017-05-15 16:45:49 -04001826class EllipseOp : public GrMeshDrawOp {
1827private:
1828 using Helper = GrSimpleMeshDrawOpHelper;
1829
1830 struct DeviceSpaceParams {
1831 SkPoint fCenter;
1832 SkScalar fXRadius;
1833 SkScalar fYRadius;
1834 SkScalar fInnerXRadius;
1835 SkScalar fInnerYRadius;
1836 };
1837
joshualitt76e7fb62015-02-11 08:52:27 -08001838public:
Brian Salomon25a88092016-12-01 09:36:50 -05001839 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001840
Herb Derbyc76d4092020-10-07 16:46:15 -04001841 static GrOp::Owner Make(GrRecordingContext* context,
1842 GrPaint&& paint,
1843 const SkMatrix& viewMatrix,
1844 const SkRect& ellipse,
1845 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001846 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001847 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001848 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1849 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1851 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001852 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1853 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1854 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1855 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001856
bsalomon4b4a7cc2016-07-08 04:42:54 -07001857 // do (potentially) anisotropic mapping of stroke
1858 SkVector scaledStroke;
1859 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001860 scaledStroke.fX = SkScalarAbs(
1861 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1862 scaledStroke.fY = SkScalarAbs(
1863 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001864
1865 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001866 bool isStrokeOnly =
1867 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001868 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1869
Brian Salomon05441c42017-05-15 16:45:49 -04001870 params.fInnerXRadius = 0;
1871 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001872 if (hasStroke) {
1873 if (SkScalarNearlyZero(scaledStroke.length())) {
1874 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1875 } else {
1876 scaledStroke.scale(SK_ScalarHalf);
1877 }
1878
1879 // we only handle thick strokes for near-circular ellipses
1880 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001881 (0.5f * params.fXRadius > params.fYRadius ||
1882 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001883 return nullptr;
1884 }
1885
1886 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001887 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1888 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1889 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1890 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001891 return nullptr;
1892 }
1893
1894 // this is legit only if scale & translation (which should be the case at the moment)
1895 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001896 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1897 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001898 }
1899
Brian Salomon05441c42017-05-15 16:45:49 -04001900 params.fXRadius += scaledStroke.fX;
1901 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001902 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001903
1904 // For large ovals with low precision floats, we fall back to the path renderer.
1905 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1906 // minimum value to avoid divides by zero. With large ovals and low precision this
1907 // leads to blurring at the edge of the oval.
1908 const SkScalar kMaxOvalRadius = 16384;
1909 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1910 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1911 return nullptr;
1912 }
1913
Greg Daniel2655ede2019-04-10 00:49:28 +00001914 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001915 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001916 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001917
Herb Derbyc76d4092020-10-07 16:46:15 -04001918 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001919 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001920 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001921 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001922 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001923 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001924 SkStrokeRec::Style style = stroke.getStyle();
1925 bool isStrokeOnly =
1926 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001927
Brian Salomon05441c42017-05-15 16:45:49 -04001928 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1929 params.fInnerXRadius, params.fInnerYRadius,
1930 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1931 params.fCenter.fY - params.fYRadius,
1932 params.fCenter.fX + params.fXRadius,
1933 params.fCenter.fY + params.fYRadius)});
1934
Greg Daniel5faf4742019-10-01 15:14:44 -04001935 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001936
bsalomon4b4a7cc2016-07-08 04:42:54 -07001937 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001938 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001939
Brian Salomon05441c42017-05-15 16:45:49 -04001940 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1941 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001942 }
joshualitt76e7fb62015-02-11 08:52:27 -08001943
Brian Salomon289e3d82016-12-14 15:52:56 -05001944 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001945
Chris Dalton1706cbf2019-05-21 19:35:29 -06001946 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001947 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001948 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001949 } else {
1950 fHelper.visitProxies(func);
1951 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001952 }
1953
Chris Dalton57ab06c2021-04-22 12:57:28 -06001954 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1955 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001956 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1957 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001958 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06001959 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001960 GrProcessorAnalysisCoverage::kSingleChannel, color,
1961 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001962 }
1963
1964 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1965
bsalomone46f9fe2015-08-18 06:05:14 -07001966private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001967 GrProgramInfo* programInfo() override { return fProgramInfo; }
1968
Robert Phillips4133dc42020-03-11 15:55:55 -04001969 void onCreateProgramInfo(const GrCaps* caps,
1970 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001971 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001972 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04001973 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001974 GrXferBarrierFlags renderPassXferBarriers,
1975 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001976 SkMatrix localMatrix;
1977 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001978 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001979 }
1980
Robert Phillips4490d922020-03-03 14:50:59 -05001981 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1982 fUseScale, localMatrix);
1983
Brian Salomon8afde5f2020-04-01 16:22:00 -04001984 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001985 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001986 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001987 }
1988
Robert Phillips4490d922020-03-03 14:50:59 -05001989 void onPrepareDraws(Target* target) override {
1990 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001991 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001992 if (!fProgramInfo) {
1993 return;
1994 }
1995 }
1996
Robert Phillips787fd9d2021-03-22 14:48:09 -04001997 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001998 GrVertexWriter verts{helper.vertices()};
1999 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05002000 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08002001 return;
2002 }
2003
Brian Salomon05441c42017-05-15 16:45:49 -04002004 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002005 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002006 SkScalar xRadius = ellipse.fXRadius;
2007 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002008
2009 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002010 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2011 SkScalarInvert(xRadius),
2012 SkScalarInvert(yRadius),
2013 SkScalarInvert(ellipse.fInnerXRadius),
2014 SkScalarInvert(ellipse.fInnerYRadius)
2015 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002016 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2017 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002018
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002019 if (!fStroked) {
2020 // For filled ellipses we map a unit circle in the vertex attributes rather than
2021 // computing an ellipse and modifying that distance, so we normalize to 1
2022 xMaxOffset /= xRadius;
2023 yMaxOffset /= yRadius;
2024 }
2025
joshualitt76e7fb62015-02-11 08:52:27 -08002026 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002027 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2028 color,
2029 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002030 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002031 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002032 }
Robert Phillips4490d922020-03-03 14:50:59 -05002033 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002034 }
2035
2036 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002037 if (!fProgramInfo || !fMesh) {
2038 return;
2039 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002040
Chris Dalton765ed362020-03-16 17:34:44 -06002041 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002042 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002043 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002044 }
2045
Herb Derbye25c3002020-10-27 15:57:27 -04002046 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002047 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002048
Brian Salomon05441c42017-05-15 16:45:49 -04002049 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002050 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002051 }
2052
bsalomoncdaa97b2016-03-08 08:30:14 -08002053 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002054 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002055 }
2056
Brian Salomon05441c42017-05-15 16:45:49 -04002057 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002058 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2059 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002060 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002061 }
2062
Brian Salomon05441c42017-05-15 16:45:49 -04002063 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002064 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002065 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002066 }
2067
John Stilesaf366522020-08-13 09:57:34 -04002068#if GR_TEST_UTILS
2069 SkString onDumpInfo() const override {
2070 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2071 for (const auto& geo : fEllipses) {
2072 string.appendf(
2073 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2074 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2075 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2076 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2077 geo.fInnerXRadius, geo.fInnerYRadius);
2078 }
2079 string += fHelper.dumpInfo();
2080 return string;
2081 }
2082#endif
2083
Brian Salomon05441c42017-05-15 16:45:49 -04002084 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002085 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002086 SkScalar fXRadius;
2087 SkScalar fYRadius;
2088 SkScalar fInnerXRadius;
2089 SkScalar fInnerYRadius;
2090 SkRect fDevBounds;
2091 };
joshualitt76e7fb62015-02-11 08:52:27 -08002092
Brian Salomon289e3d82016-12-14 15:52:56 -05002093 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002094 Helper fHelper;
2095 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002096 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002097 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002098 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002099
Chris Daltoneb694b72020-03-16 09:25:50 -06002100 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002101 GrProgramInfo* fProgramInfo = nullptr;
2102
John Stiles7571f9e2020-09-02 22:42:33 -04002103 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002104};
2105
joshualitt76e7fb62015-02-11 08:52:27 -08002106/////////////////////////////////////////////////////////////////////////////////////////////////
2107
Brian Salomon05441c42017-05-15 16:45:49 -04002108class DIEllipseOp : public GrMeshDrawOp {
2109private:
2110 using Helper = GrSimpleMeshDrawOpHelper;
2111
2112 struct DeviceSpaceParams {
2113 SkPoint fCenter;
2114 SkScalar fXRadius;
2115 SkScalar fYRadius;
2116 SkScalar fInnerXRadius;
2117 SkScalar fInnerYRadius;
2118 DIEllipseStyle fStyle;
2119 };
2120
joshualitt76e7fb62015-02-11 08:52:27 -08002121public:
Brian Salomon25a88092016-12-01 09:36:50 -05002122 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002123
Herb Derbyc76d4092020-10-07 16:46:15 -04002124 static GrOp::Owner Make(GrRecordingContext* context,
2125 GrPaint&& paint,
2126 const SkMatrix& viewMatrix,
2127 const SkRect& ellipse,
2128 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002129 DeviceSpaceParams params;
2130 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2131 params.fXRadius = SkScalarHalf(ellipse.width());
2132 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002133
bsalomon4b4a7cc2016-07-08 04:42:54 -07002134 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002135 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2136 ? DIEllipseStyle::kStroke
2137 : (SkStrokeRec::kHairline_Style == style)
2138 ? DIEllipseStyle::kHairline
2139 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002140
Brian Salomon05441c42017-05-15 16:45:49 -04002141 params.fInnerXRadius = 0;
2142 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002143 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2144 SkScalar strokeWidth = stroke.getWidth();
2145
2146 if (SkScalarNearlyZero(strokeWidth)) {
2147 strokeWidth = SK_ScalarHalf;
2148 } else {
2149 strokeWidth *= SK_ScalarHalf;
2150 }
2151
2152 // we only handle thick strokes for near-circular ellipses
2153 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002154 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2155 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002156 return nullptr;
2157 }
2158
2159 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002160 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2161 (strokeWidth * strokeWidth) * params.fXRadius) {
2162 return nullptr;
2163 }
2164 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2165 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002166 return nullptr;
2167 }
2168
2169 // set inner radius (if needed)
2170 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002171 params.fInnerXRadius = params.fXRadius - strokeWidth;
2172 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002173 }
2174
Brian Salomon05441c42017-05-15 16:45:49 -04002175 params.fXRadius += strokeWidth;
2176 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002177 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002178
2179 // For large ovals with low precision floats, we fall back to the path renderer.
2180 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2181 // minimum value to avoid divides by zero. With large ovals and low precision this
2182 // leads to blurring at the edge of the oval.
2183 const SkScalar kMaxOvalRadius = 16384;
2184 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2185 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2186 return nullptr;
2187 }
2188
Brian Salomon05441c42017-05-15 16:45:49 -04002189 if (DIEllipseStyle::kStroke == params.fStyle &&
2190 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2191 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002192 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002193 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002194 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002195
Herb Derbyc76d4092020-10-07 16:46:15 -04002196 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002197 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002198 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002199 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002200 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002201 // This expands the outer rect so that after CTM we end up with a half-pixel border
2202 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2203 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2204 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2205 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2206 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2207 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002208
Brian Salomon05441c42017-05-15 16:45:49 -04002209 fEllipses.emplace_back(
2210 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2211 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2212 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2213 params.fCenter.fY - params.fYRadius - geoDy,
2214 params.fCenter.fX + params.fXRadius + geoDx,
2215 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002216 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002217 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002218 }
2219
Brian Salomon289e3d82016-12-14 15:52:56 -05002220 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002221
Chris Dalton1706cbf2019-05-21 19:35:29 -06002222 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002223 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002224 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002225 } else {
2226 fHelper.visitProxies(func);
2227 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002228 }
2229
Chris Dalton57ab06c2021-04-22 12:57:28 -06002230 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2231 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002232 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2233 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002234 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002235 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002236 GrProcessorAnalysisCoverage::kSingleChannel, color,
2237 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002238 }
2239
2240 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2241
bsalomone46f9fe2015-08-18 06:05:14 -07002242private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002243 GrProgramInfo* programInfo() override { return fProgramInfo; }
2244
Robert Phillips4133dc42020-03-11 15:55:55 -04002245 void onCreateProgramInfo(const GrCaps* caps,
2246 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002247 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002248 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002249 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002250 GrXferBarrierFlags renderPassXferBarriers,
2251 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002252 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2253 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002254 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002255
Brian Salomon8afde5f2020-04-01 16:22:00 -04002256 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002257 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002258 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002259 }
2260
Robert Phillips4490d922020-03-03 14:50:59 -05002261 void onPrepareDraws(Target* target) override {
2262 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002263 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002264 }
2265
Robert Phillips787fd9d2021-03-22 14:48:09 -04002266 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002267 GrVertexWriter verts{helper.vertices()};
2268 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002269 return;
2270 }
2271
Brian Salomon05441c42017-05-15 16:45:49 -04002272 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002273 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002274 SkScalar xRadius = ellipse.fXRadius;
2275 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002276
joshualitt76e7fb62015-02-11 08:52:27 -08002277 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002278 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2279 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002280
Brian Osman9d958b52018-11-13 12:46:56 -05002281 // By default, constructed so that inner offset is (0, 0) for all points
2282 SkScalar innerRatioX = -offsetDx;
2283 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002284
Brian Osman9d958b52018-11-13 12:46:56 -05002285 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002286 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002287 innerRatioX = xRadius / ellipse.fInnerXRadius;
2288 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002289 }
joshualitt76e7fb62015-02-11 08:52:27 -08002290
Brian Osman2b6e3902018-11-21 15:29:43 -05002291 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2292 color,
2293 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002294 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002295 origin_centered_tri_strip(innerRatioX + offsetDx,
2296 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002297 }
Robert Phillips4490d922020-03-03 14:50:59 -05002298 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002299 }
2300
2301 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002302 if (!fProgramInfo || !fMesh) {
2303 return;
2304 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002305
Chris Dalton765ed362020-03-16 17:34:44 -06002306 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002307 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002308 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002309 }
halcanary9d524f22016-03-29 09:03:52 -07002310
Herb Derbye25c3002020-10-27 15:57:27 -04002311 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002312 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002313 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002314 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002315 }
2316
bsalomoncdaa97b2016-03-08 08:30:14 -08002317 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002318 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002319 }
2320
joshualittd96a67b2015-05-05 14:09:05 -07002321 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002322 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002323 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002324 }
2325
Brian Salomon05441c42017-05-15 16:45:49 -04002326 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002327 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002328 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002329 }
2330
John Stilesaf366522020-08-13 09:57:34 -04002331#if GR_TEST_UTILS
2332 SkString onDumpInfo() const override {
2333 SkString string;
2334 for (const auto& geo : fEllipses) {
2335 string.appendf(
2336 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2337 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2338 "GeoDY: %.2f\n",
2339 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2340 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2341 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2342 }
2343 string += fHelper.dumpInfo();
2344 return string;
2345 }
2346#endif
2347
Brian Salomon05441c42017-05-15 16:45:49 -04002348 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2349 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002350
Brian Salomon05441c42017-05-15 16:45:49 -04002351 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002352 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002353 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002354 SkScalar fXRadius;
2355 SkScalar fYRadius;
2356 SkScalar fInnerXRadius;
2357 SkScalar fInnerYRadius;
2358 SkScalar fGeoDx;
2359 SkScalar fGeoDy;
2360 DIEllipseStyle fStyle;
2361 SkRect fBounds;
2362 };
2363
Brian Salomon05441c42017-05-15 16:45:49 -04002364 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002365 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002366 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002367 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002368
Chris Daltoneb694b72020-03-16 09:25:50 -06002369 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002370 GrProgramInfo* fProgramInfo = nullptr;
2371
John Stiles7571f9e2020-09-02 22:42:33 -04002372 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002373};
2374
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002375///////////////////////////////////////////////////////////////////////////////
2376
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002377// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002378//
2379// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2380// ____________
2381// |_|________|_|
2382// | | | |
2383// | | | |
2384// | | | |
2385// |_|________|_|
2386// |_|________|_|
2387//
2388// For strokes, we don't draw the center quad.
2389//
2390// For circular roundrects, in the case where the stroke width is greater than twice
2391// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002392// in the center. The shared vertices are duplicated so we can set a different outer radius
2393// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002394// ____________
2395// |_|________|_|
2396// | |\ ____ /| |
2397// | | | | | |
2398// | | |____| | |
2399// |_|/______\|_|
2400// |_|________|_|
2401//
2402// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002403//
2404// For filled rrects that need to provide a distance vector we resuse the overstroke
2405// geometry but make the inner rect degenerate (either a point or a horizontal or
2406// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002407
jvanverth84839f62016-08-29 10:16:40 -07002408static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002409 // clang-format off
2410 // overstroke quads
2411 // we place this at the beginning so that we can skip these indices when rendering normally
2412 16, 17, 19, 16, 19, 18,
2413 19, 17, 23, 19, 23, 21,
2414 21, 23, 22, 21, 22, 20,
2415 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002416
Brian Salomon289e3d82016-12-14 15:52:56 -05002417 // corners
2418 0, 1, 5, 0, 5, 4,
2419 2, 3, 7, 2, 7, 6,
2420 8, 9, 13, 8, 13, 12,
2421 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002422
Brian Salomon289e3d82016-12-14 15:52:56 -05002423 // edges
2424 1, 2, 6, 1, 6, 5,
2425 4, 5, 9, 4, 9, 8,
2426 6, 7, 11, 6, 11, 10,
2427 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002428
Brian Salomon289e3d82016-12-14 15:52:56 -05002429 // center
2430 // we place this at the end so that we can ignore these indices when not rendering as filled
2431 5, 6, 10, 5, 10, 9,
2432 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002433};
Brian Salomon289e3d82016-12-14 15:52:56 -05002434
jvanverth84839f62016-08-29 10:16:40 -07002435// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002436static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002437
jvanverth84839f62016-08-29 10:16:40 -07002438// overstroke count is arraysize minus the center indices
2439static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2440// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002441static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002442// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002443static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2444static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002445static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002446
jvanverthc3d0e422016-08-25 08:12:35 -07002447enum RRectType {
2448 kFill_RRectType,
2449 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002450 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002451};
2452
jvanverth84839f62016-08-29 10:16:40 -07002453static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002454 switch (type) {
2455 case kFill_RRectType:
2456 case kStroke_RRectType:
2457 return kVertsPerStandardRRect;
2458 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002459 return kVertsPerOverstrokeRRect;
2460 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002461 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002462}
2463
2464static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002465 switch (type) {
2466 case kFill_RRectType:
2467 return kIndicesPerFillRRect;
2468 case kStroke_RRectType:
2469 return kIndicesPerStrokeRRect;
2470 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002471 return kIndicesPerOverstrokeRRect;
2472 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002473 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002474}
2475
2476static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002477 switch (type) {
2478 case kFill_RRectType:
2479 case kStroke_RRectType:
2480 return gStandardRRectIndices;
2481 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002482 return gOverstrokeRRectIndices;
2483 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002484 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002485}
2486
joshualitt76e7fb62015-02-11 08:52:27 -08002487///////////////////////////////////////////////////////////////////////////////////////////////////
2488
Robert Phillips79839d42016-10-06 15:03:34 -04002489// For distance computations in the interior of filled rrects we:
2490//
2491// add a interior degenerate (point or line) rect
2492// each vertex of that rect gets -outerRad as its radius
2493// this makes the computation of the distance to the outer edge be negative
2494// negative values are caught and then handled differently in the GP's onEmitCode
2495// each vertex is also given the normalized x & y distance from the interior rect's edge
2496// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2497
Brian Salomon05441c42017-05-15 16:45:49 -04002498class CircularRRectOp : public GrMeshDrawOp {
2499private:
2500 using Helper = GrSimpleMeshDrawOpHelper;
2501
joshualitt76e7fb62015-02-11 08:52:27 -08002502public:
Brian Salomon25a88092016-12-01 09:36:50 -05002503 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002504
bsalomon4b4a7cc2016-07-08 04:42:54 -07002505 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2506 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002507 static GrOp::Owner Make(GrRecordingContext* context,
2508 GrPaint&& paint,
2509 const SkMatrix& viewMatrix,
2510 const SkRect& devRect,
2511 float devRadius,
2512 float devStrokeWidth,
2513 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002514 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002515 devRect, devRadius,
2516 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002517 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002518 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002519 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2520 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002521 : INHERITED(ClassID())
2522 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002523 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002524 SkRect bounds = devRect;
2525 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2526 SkScalar innerRadius = 0.0f;
2527 SkScalar outerRadius = devRadius;
2528 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002529 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002530 if (devStrokeWidth > 0) {
2531 if (SkScalarNearlyZero(devStrokeWidth)) {
2532 halfWidth = SK_ScalarHalf;
2533 } else {
2534 halfWidth = SkScalarHalf(devStrokeWidth);
2535 }
joshualitt76e7fb62015-02-11 08:52:27 -08002536
bsalomon4b4a7cc2016-07-08 04:42:54 -07002537 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002538 // Outset stroke by 1/4 pixel
2539 devStrokeWidth += 0.25f;
2540 // If stroke is greater than width or height, this is still a fill
2541 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002542 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002543 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002544 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002545 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002546 }
2547 outerRadius += halfWidth;
2548 bounds.outset(halfWidth, halfWidth);
2549 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002550
Greg Daniel2655ede2019-04-10 00:49:28 +00002551 // The radii are outset for two reasons. First, it allows the shader to simply perform
2552 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2553 // Second, the outer radius is used to compute the verts of the bounding box that is
2554 // rendered and the outset ensures the box will cover all partially covered by the rrect
2555 // corners.
2556 outerRadius += SK_ScalarHalf;
2557 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002558
Greg Daniel5faf4742019-10-01 15:14:44 -04002559 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002560
Greg Daniel2655ede2019-04-10 00:49:28 +00002561 // Expand the rect for aa to generate correct vertices.
2562 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002563
Brian Salomon05441c42017-05-15 16:45:49 -04002564 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002565 fVertCount = rrect_type_to_vert_count(type);
2566 fIndexCount = rrect_type_to_index_count(type);
2567 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002568 }
2569
Brian Salomon289e3d82016-12-14 15:52:56 -05002570 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002571
Chris Dalton1706cbf2019-05-21 19:35:29 -06002572 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002573 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002574 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002575 } else {
2576 fHelper.visitProxies(func);
2577 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002578 }
2579
Chris Dalton57ab06c2021-04-22 12:57:28 -06002580 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2581 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002582 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002583 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002584 GrProcessorAnalysisCoverage::kSingleChannel, color,
2585 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002586 }
2587
2588 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2589
Brian Salomon92aee3d2016-12-21 09:20:25 -05002590private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002591 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002592 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002593 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002594 SkASSERT(smInset < bigInset);
2595
2596 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002597 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2598 color,
2599 xOffset, 0.0f,
2600 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002601
2602 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002603 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2604 color,
2605 xOffset, 0.0f,
2606 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002607
Brian Osmana1d4eb92018-12-06 16:33:10 -05002608 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2609 color,
2610 0.0f, 0.0f,
2611 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002612
Brian Osmana1d4eb92018-12-06 16:33:10 -05002613 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2614 color,
2615 0.0f, 0.0f,
2616 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002617
Brian Osmana1d4eb92018-12-06 16:33:10 -05002618 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2619 color,
2620 0.0f, 0.0f,
2621 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002622
Brian Osmana1d4eb92018-12-06 16:33:10 -05002623 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2624 color,
2625 0.0f, 0.0f,
2626 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002627
2628 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002629 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2630 color,
2631 xOffset, 0.0f,
2632 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002633
2634 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002635 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2636 color,
2637 xOffset, 0.0f,
2638 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002639 }
2640
Robert Phillips2669a7b2020-03-12 12:07:19 -04002641 GrProgramInfo* programInfo() override { return fProgramInfo; }
2642
Robert Phillips4133dc42020-03-11 15:55:55 -04002643 void onCreateProgramInfo(const GrCaps* caps,
2644 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002645 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002646 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002647 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002648 GrXferBarrierFlags renderPassXferBarriers,
2649 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002650 // Invert the view matrix as a local matrix (if any other processors require coords).
2651 SkMatrix localMatrix;
2652 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002653 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002654 }
2655
Robert Phillips4490d922020-03-03 14:50:59 -05002656 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002657 false, false, false, false,
2658 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002659
Brian Salomon8afde5f2020-04-01 16:22:00 -04002660 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002661 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002662 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002663 }
2664
Robert Phillips4490d922020-03-03 14:50:59 -05002665 void onPrepareDraws(Target* target) override {
2666 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002667 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002668 if (!fProgramInfo) {
2669 return;
2670 }
2671 }
2672
Brian Salomon12d22642019-01-29 14:38:50 -05002673 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002674 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002675
Robert Phillips787fd9d2021-03-22 14:48:09 -04002676 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002677 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002678 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002679 SkDebugf("Could not allocate vertices\n");
2680 return;
2681 }
2682
Brian Salomon12d22642019-01-29 14:38:50 -05002683 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002684 int firstIndex = 0;
2685 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2686 if (!indices) {
2687 SkDebugf("Could not allocate indices\n");
2688 return;
2689 }
2690
2691 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002692 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002693 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002694 SkScalar outerRadius = rrect.fOuterRadius;
2695 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002696
Brian Salomon289e3d82016-12-14 15:52:56 -05002697 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2698 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002699
Brian Salomon289e3d82016-12-14 15:52:56 -05002700 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002701 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002702 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002703 SkScalar innerRadius = rrect.fType != kFill_RRectType
2704 ? rrect.fInnerRadius / rrect.fOuterRadius
2705 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002706 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002707 verts.write(bounds.fLeft, yCoords[i],
2708 color,
2709 -1.0f, yOuterRadii[i],
2710 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002711
Brian Osmana1d4eb92018-12-06 16:33:10 -05002712 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2713 color,
2714 0.0f, yOuterRadii[i],
2715 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002716
Brian Osmana1d4eb92018-12-06 16:33:10 -05002717 verts.write(bounds.fRight - 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, yCoords[i],
2723 color,
2724 1.0f, yOuterRadii[i],
2725 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002726 }
jvanverthc3d0e422016-08-25 08:12:35 -07002727 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002728 // Effectively this is an additional stroked rrect, with its
2729 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2730 // This will give us correct AA in the center and the correct
2731 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002732 //
jvanvertha4f1af82016-08-29 07:17:47 -07002733 // Also, the outer offset is a constant vector pointing to the right, which
2734 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002735 if (kOverstroke_RRectType == rrect.fType) {
2736 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002737
Brian Salomon05441c42017-05-15 16:45:49 -04002738 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002739 // this is the normalized distance from the outer rectangle of this
2740 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002741 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002742
Brian Osmana1d4eb92018-12-06 16:33:10 -05002743 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002744 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002745 }
jvanverth6a397612016-08-26 08:15:33 -07002746
Brian Salomon05441c42017-05-15 16:45:49 -04002747 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2748 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002749 for (int i = 0; i < primIndexCount; ++i) {
2750 *indices++ = primIndices[i] + currStartVertex;
2751 }
2752
Brian Salomon05441c42017-05-15 16:45:49 -04002753 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002754 }
2755
Robert Phillips4490d922020-03-03 14:50:59 -05002756 fMesh = target->allocMesh();
2757 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002758 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002759 }
2760
2761 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002762 if (!fProgramInfo || !fMesh) {
2763 return;
2764 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002765
Chris Dalton765ed362020-03-16 17:34:44 -06002766 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002767 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002768 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002769 }
2770
Herb Derbye25c3002020-10-27 15:57:27 -04002771 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002772 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002773
2774 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002775 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002776 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002777 }
2778
Brian Salomon05441c42017-05-15 16:45:49 -04002779 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002780 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002781 }
2782
Brian Salomon05441c42017-05-15 16:45:49 -04002783 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002784 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2785 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002786 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002787 }
2788
Brian Salomon05441c42017-05-15 16:45:49 -04002789 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002790 fVertCount += that->fVertCount;
2791 fIndexCount += that->fIndexCount;
2792 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002793 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002794 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002795 }
2796
John Stilesaf366522020-08-13 09:57:34 -04002797#if GR_TEST_UTILS
2798 SkString onDumpInfo() const override {
2799 SkString string;
2800 for (int i = 0; i < fRRects.count(); ++i) {
2801 string.appendf(
2802 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2803 "InnerRad: %.2f, OuterRad: %.2f\n",
2804 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2805 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2806 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2807 fRRects[i].fOuterRadius);
2808 }
2809 string += fHelper.dumpInfo();
2810 return string;
2811 }
2812#endif
2813
Brian Salomon05441c42017-05-15 16:45:49 -04002814 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002815 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002816 SkScalar fInnerRadius;
2817 SkScalar fOuterRadius;
2818 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002819 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002820 };
2821
Brian Salomon289e3d82016-12-14 15:52:56 -05002822 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002823 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002824 int fVertCount;
2825 int fIndexCount;
2826 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002827 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002828 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002829
Chris Daltoneb694b72020-03-16 09:25:50 -06002830 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002831 GrProgramInfo* fProgramInfo = nullptr;
2832
John Stiles7571f9e2020-09-02 22:42:33 -04002833 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002834};
2835
jvanverth84839f62016-08-29 10:16:40 -07002836static const int kNumRRectsInIndexBuffer = 256;
2837
2838GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2839GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002840static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2841 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002842 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2843 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2844 switch (type) {
2845 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002846 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002847 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2848 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002849 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002850 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002851 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2852 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002853 default:
2854 SkASSERT(false);
2855 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002856 }
jvanverth84839f62016-08-29 10:16:40 -07002857}
2858
Brian Salomon05441c42017-05-15 16:45:49 -04002859class EllipticalRRectOp : public GrMeshDrawOp {
2860private:
2861 using Helper = GrSimpleMeshDrawOpHelper;
2862
joshualitt76e7fb62015-02-11 08:52:27 -08002863public:
Brian Salomon25a88092016-12-01 09:36:50 -05002864 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002865
bsalomon4b4a7cc2016-07-08 04:42:54 -07002866 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2867 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002868 static GrOp::Owner Make(GrRecordingContext* context,
2869 GrPaint&& paint,
2870 const SkMatrix& viewMatrix,
2871 const SkRect& devRect,
2872 float devXRadius,
2873 float devYRadius,
2874 SkVector devStrokeWidths,
2875 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002876 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2877 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002878 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2879 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002880 if (devStrokeWidths.fX > 0) {
2881 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2882 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2883 } else {
2884 devStrokeWidths.scale(SK_ScalarHalf);
2885 }
joshualitt76e7fb62015-02-11 08:52:27 -08002886
bsalomon4b4a7cc2016-07-08 04:42:54 -07002887 // we only handle thick strokes for near-circular ellipses
2888 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002889 (SK_ScalarHalf * devXRadius > devYRadius ||
2890 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002891 return nullptr;
2892 }
2893
2894 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002895 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2896 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002897 return nullptr;
2898 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002899 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2900 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002901 return nullptr;
2902 }
Brian Salomon05441c42017-05-15 16:45:49 -04002903 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002904 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002905 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002906 devXRadius, devYRadius, devStrokeWidths,
2907 strokeOnly);
2908 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002909
Herb Derbyc76d4092020-10-07 16:46:15 -04002910 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002911 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2912 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002913 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002914 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002915 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002916 SkScalar innerXRadius = 0.0f;
2917 SkScalar innerYRadius = 0.0f;
2918 SkRect bounds = devRect;
2919 bool stroked = false;
2920 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002921 // this is legit only if scale & translation (which should be the case at the moment)
2922 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002923 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2924 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002925 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2926 }
2927
Brian Salomon05441c42017-05-15 16:45:49 -04002928 devXRadius += devStrokeHalfWidths.fX;
2929 devYRadius += devStrokeHalfWidths.fY;
2930 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002931 }
2932
Brian Salomon05441c42017-05-15 16:45:49 -04002933 fStroked = stroked;
2934 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002935 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002936 // Expand the rect for aa in order to generate the correct vertices.
2937 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002938 fRRects.emplace_back(
2939 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002940 }
2941
Brian Salomon289e3d82016-12-14 15:52:56 -05002942 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002943
Chris Dalton1706cbf2019-05-21 19:35:29 -06002944 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002945 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002946 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002947 } else {
2948 fHelper.visitProxies(func);
2949 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002950 }
2951
Chris Dalton57ab06c2021-04-22 12:57:28 -06002952 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2953 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002954 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002955 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton57ab06c2021-04-22 12:57:28 -06002956 return fHelper.finalizeProcessors(caps, clip, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002957 GrProcessorAnalysisCoverage::kSingleChannel, color,
2958 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002959 }
2960
2961 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2962
bsalomone46f9fe2015-08-18 06:05:14 -07002963private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002964 GrProgramInfo* programInfo() override { return fProgramInfo; }
2965
Robert Phillips4133dc42020-03-11 15:55:55 -04002966 void onCreateProgramInfo(const GrCaps* caps,
2967 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002968 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002969 GrAppliedClip&& appliedClip,
John Stiles52cb1d02021-06-02 11:58:05 -04002970 const GrDstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002971 GrXferBarrierFlags renderPassXferBarriers,
2972 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002973 SkMatrix localMatrix;
2974 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002975 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002976 }
2977
Robert Phillips4490d922020-03-03 14:50:59 -05002978 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2979 fUseScale, localMatrix);
2980
Brian Salomon8afde5f2020-04-01 16:22:00 -04002981 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002982 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002983 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002984 }
2985
Robert Phillips4490d922020-03-03 14:50:59 -05002986 void onPrepareDraws(Target* target) override {
2987 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002988 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002989 if (!fProgramInfo) {
2990 return;
2991 }
2992 }
joshualitt76e7fb62015-02-11 08:52:27 -08002993
bsalomonb5238a72015-05-05 07:49:49 -07002994 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002995 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002996 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2997 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002998
Brian Salomon12d22642019-01-29 14:38:50 -05002999 if (!indexBuffer) {
3000 SkDebugf("Could not allocate indices\n");
3001 return;
3002 }
Robert Phillips4490d922020-03-03 14:50:59 -05003003 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003004 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003005 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003006 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003007 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003008 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003009 SkDebugf("Could not allocate vertices\n");
3010 return;
3011 }
3012
Brian Salomon05441c42017-05-15 16:45:49 -04003013 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003014 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003015 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003016 float reciprocalRadii[4] = {
3017 SkScalarInvert(rrect.fXRadius),
3018 SkScalarInvert(rrect.fYRadius),
3019 SkScalarInvert(rrect.fInnerXRadius),
3020 SkScalarInvert(rrect.fInnerYRadius)
3021 };
joshualitt76e7fb62015-02-11 08:52:27 -08003022
Brian Osmane3afdd52020-10-28 10:49:56 -04003023 // If the stroke width is exactly double the radius, the inner radii will be zero.
3024 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3025 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3026 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3027
joshualitt76e7fb62015-02-11 08:52:27 -08003028 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003029 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3030 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003031
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003032 SkScalar xMaxOffset = xOuterRadius;
3033 SkScalar yMaxOffset = yOuterRadius;
3034 if (!fStroked) {
3035 // For filled rrects we map a unit circle in the vertex attributes rather than
3036 // computing an ellipse and modifying that distance, so we normalize to 1.
3037 xMaxOffset /= rrect.fXRadius;
3038 yMaxOffset /= rrect.fYRadius;
3039 }
3040
Brian Salomon05441c42017-05-15 16:45:49 -04003041 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003042
Brian Salomon289e3d82016-12-14 15:52:56 -05003043 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3044 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003045 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003046 SK_ScalarNearlyZero, // we're using inversesqrt() in
3047 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003048 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003049
Brian Osman788b9162020-02-07 10:36:46 -05003050 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003051 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003052 verts.write(bounds.fLeft, yCoords[i],
3053 color,
3054 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003055 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003056 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003057
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3059 color,
3060 SK_ScalarNearlyZero, 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.fRight - 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, yCoords[i],
3071 color,
3072 xMaxOffset, 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 }
3076 }
Robert Phillips4490d922020-03-03 14:50:59 -05003077 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003078 }
3079
3080 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003081 if (!fProgramInfo || !fMesh) {
3082 return;
3083 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003084
Chris Dalton765ed362020-03-16 17:34:44 -06003085 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003086 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003087 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003088 }
3089
Herb Derbye25c3002020-10-27 15:57:27 -04003090 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003091 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003092
Brian Salomon05441c42017-05-15 16:45:49 -04003093 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003094 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003095 }
3096
bsalomoncdaa97b2016-03-08 08:30:14 -08003097 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003098 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003099 }
3100
Brian Salomon05441c42017-05-15 16:45:49 -04003101 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003102 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3103 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003104 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003105 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003106
Brian Salomon05441c42017-05-15 16:45:49 -04003107 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003108 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003109 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003110 }
3111
John Stilesaf366522020-08-13 09:57:34 -04003112#if GR_TEST_UTILS
3113 SkString onDumpInfo() const override {
3114 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3115 for (const auto& geo : fRRects) {
3116 string.appendf(
3117 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3118 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3119 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3120 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3121 geo.fInnerXRadius, geo.fInnerYRadius);
3122 }
3123 string += fHelper.dumpInfo();
3124 return string;
3125 }
3126#endif
3127
Brian Salomon05441c42017-05-15 16:45:49 -04003128 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003129 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003130 SkScalar fXRadius;
3131 SkScalar fYRadius;
3132 SkScalar fInnerXRadius;
3133 SkScalar fInnerYRadius;
3134 SkRect fDevBounds;
3135 };
3136
Brian Salomon289e3d82016-12-14 15:52:56 -05003137 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003138 Helper fHelper;
3139 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003140 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003141 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003142 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003143
Chris Daltoneb694b72020-03-16 09:25:50 -06003144 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003145 GrProgramInfo* fProgramInfo = nullptr;
3146
John Stiles7571f9e2020-09-02 22:42:33 -04003147 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003148};
3149
Herb Derbyc76d4092020-10-07 16:46:15 -04003150GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3151 GrPaint&& paint,
3152 const SkMatrix& viewMatrix,
3153 const SkRRect& rrect,
3154 const SkStrokeRec& stroke,
3155 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003156 SkASSERT(viewMatrix.rectStaysRect());
3157 SkASSERT(viewMatrix.isSimilarity());
3158 SkASSERT(rrect.isSimple());
3159 SkASSERT(!rrect.isOval());
3160 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3161
3162 // RRect ops only handle simple, but not too simple, rrects.
3163 // Do any matrix crunching before we reset the draw state for device coords.
3164 const SkRect& rrectBounds = rrect.getBounds();
3165 SkRect bounds;
3166 viewMatrix.mapRect(&bounds, rrectBounds);
3167
3168 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3169 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3170 viewMatrix[SkMatrix::kMSkewY]));
3171
3172 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3173 SkScalar scaledStroke = -1;
3174 SkScalar strokeWidth = stroke.getWidth();
3175 SkStrokeRec::Style style = stroke.getStyle();
3176
3177 bool isStrokeOnly =
3178 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3179 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3180
3181 if (hasStroke) {
3182 if (SkStrokeRec::kHairline_Style == style) {
3183 scaledStroke = SK_Scalar1;
3184 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003185 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3186 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003187 }
3188 }
3189
3190 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3191 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3192 // patch will have fractional coverage. This only matters when the interior is actually filled.
3193 // We could consider falling back to rect rendering here, since a tiny radius is
3194 // indistinguishable from a square corner.
3195 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3196 return nullptr;
3197 }
3198
3199 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3200 scaledStroke, isStrokeOnly);
3201}
3202
Herb Derbyc76d4092020-10-07 16:46:15 -04003203GrOp::Owner make_rrect_op(GrRecordingContext* context,
3204 GrPaint&& paint,
3205 const SkMatrix& viewMatrix,
3206 const SkRRect& rrect,
3207 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003208 SkASSERT(viewMatrix.rectStaysRect());
3209 SkASSERT(rrect.isSimple());
3210 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003211
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003212 // RRect ops only handle simple, but not too simple, rrects.
3213 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003214 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003215 SkRect bounds;
3216 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003217
Mike Reed242135a2018-02-22 13:41:39 -05003218 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003219 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3220 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3221 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3222 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003223
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003224 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003225
bsalomon4b4a7cc2016-07-08 04:42:54 -07003226 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3227 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003228 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003229
Brian Salomon289e3d82016-12-14 15:52:56 -05003230 bool isStrokeOnly =
3231 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003232 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3233
3234 if (hasStroke) {
3235 if (SkStrokeRec::kHairline_Style == style) {
3236 scaledStroke.set(1, 1);
3237 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003238 scaledStroke.fX = SkScalarAbs(
3239 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3240 scaledStroke.fY = SkScalarAbs(
3241 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003242 }
3243
Jim Van Verth64b85892019-06-17 12:01:46 -04003244 // if half of strokewidth is greater than radius, we don't handle that right now
3245 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3246 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003247 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003248 }
3249 }
3250
Brian Salomon8a97f562019-04-18 14:07:27 -04003251 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003252 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003253 std::swap(xRadius, yRadius);
3254 std::swap(scaledStroke.fX, scaledStroke.fY);
3255 }
3256
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003257 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3258 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3259 // patch will have fractional coverage. This only matters when the interior is actually filled.
3260 // We could consider falling back to rect rendering here, since a tiny radius is
3261 // indistinguishable from a square corner.
3262 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003263 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003264 }
3265
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003266 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003267 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3268 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003269}
3270
Herb Derbyc76d4092020-10-07 16:46:15 -04003271GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3272 GrPaint&& paint,
3273 const SkMatrix& viewMatrix,
3274 const SkRRect& rrect,
3275 const SkStrokeRec& stroke,
3276 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003277 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003278 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003279 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003280 }
3281
3282 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003283 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003284 }
3285
Greg Daniel2655ede2019-04-10 00:49:28 +00003286 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003287}
joshualitt3e708c52015-04-30 13:49:27 -07003288
bsalomon4b4a7cc2016-07-08 04:42:54 -07003289///////////////////////////////////////////////////////////////////////////////
3290
Herb Derbyc76d4092020-10-07 16:46:15 -04003291GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3292 GrPaint&& paint,
3293 const SkMatrix& viewMatrix,
3294 const SkRect& oval,
3295 const GrStyle& style,
3296 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003297 SkScalar width = oval.width();
3298 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3299 circle_stays_circle(viewMatrix));
3300
3301 auto r = width / 2.f;
3302 SkPoint center = { oval.centerX(), oval.centerY() };
3303 if (style.hasNonDashPathEffect()) {
3304 return nullptr;
3305 } else if (style.isDashed()) {
3306 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3307 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3308 return nullptr;
3309 }
3310 auto onInterval = style.dashIntervals()[0];
3311 auto offInterval = style.dashIntervals()[1];
3312 if (offInterval == 0) {
3313 GrStyle strokeStyle(style.strokeRec(), nullptr);
3314 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3315 strokeStyle, shaderCaps);
3316 } else if (onInterval == 0) {
3317 // There is nothing to draw but we have no way to indicate that here.
3318 return nullptr;
3319 }
3320 auto angularOnInterval = onInterval / r;
3321 auto angularOffInterval = offInterval / r;
3322 auto phaseAngle = style.dashPhase() / r;
3323 // Currently this function doesn't accept ovals with different start angles, though
3324 // it could.
3325 static const SkScalar kStartAngle = 0.f;
3326 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3327 style.strokeRec().getWidth(), kStartAngle,
3328 angularOnInterval, angularOffInterval, phaseAngle);
3329 }
3330 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3331}
3332
Herb Derbyc76d4092020-10-07 16:46:15 -04003333GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3334 GrPaint&& paint,
3335 const SkMatrix& viewMatrix,
3336 const SkRect& oval,
3337 const GrStyle& style,
3338 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003339 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003340 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003341 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3342 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003343 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003344 }
3345
3346 if (style.pathEffect()) {
3347 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003348 }
3349
Stan Ilieveb868aa2017-02-21 11:06:16 -05003350 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003351 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003352 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003353 }
3354
Stan Ilieveb868aa2017-02-21 11:06:16 -05003355 // Otherwise, if we have shader derivative support, render as device-independent
3356 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003357 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3358 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3359 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3360 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3361 // Check for near-degenerate matrix
3362 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003363 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003364 style.strokeRec());
3365 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003366 }
3367
bsalomon4b4a7cc2016-07-08 04:42:54 -07003368 return nullptr;
3369}
3370
3371///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003372
Herb Derbyc76d4092020-10-07 16:46:15 -04003373GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3374 GrPaint&& paint,
3375 const SkMatrix& viewMatrix,
3376 const SkRect& oval, SkScalar startAngle,
3377 SkScalar sweepAngle, bool useCenter,
3378 const GrStyle& style,
3379 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003380 SkASSERT(!oval.isEmpty());
3381 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003382 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003383 if (SkScalarAbs(sweepAngle) >= 360.f) {
3384 return nullptr;
3385 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003386 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3387 return nullptr;
3388 }
3389 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003390 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3391 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003392 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003393 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003394}
3395
3396///////////////////////////////////////////////////////////////////////////////
3397
Hal Canary6f6961e2017-01-31 13:50:44 -05003398#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003399
Brian Salomon05441c42017-05-15 16:45:49 -04003400GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003401 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003402 SkScalar rotate = random->nextSScalar1() * 360.f;
3403 SkScalar translateX = random->nextSScalar1() * 1000.f;
3404 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003405 SkScalar scale;
3406 do {
3407 scale = random->nextSScalar1() * 100.f;
3408 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003409 SkMatrix viewMatrix;
3410 viewMatrix.setRotate(rotate);
3411 viewMatrix.postTranslate(translateX, translateY);
3412 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003413 SkRect circle = GrTest::TestSquare(random);
3414 SkPoint center = {circle.centerX(), circle.centerY()};
3415 SkScalar radius = circle.width() / 2.f;
3416 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003417 CircleOp::ArcParams arcParamsTmp;
3418 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003419 if (random->nextBool()) {
3420 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003421 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3422 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003423 arcParams = &arcParamsTmp;
3424 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003425 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3426 center, radius,
3427 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003428 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003429 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003430 }
Mike Klein16885072018-12-11 09:54:31 -05003431 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003432 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003433}
3434
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003435GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3436 SkScalar rotate = random->nextSScalar1() * 360.f;
3437 SkScalar translateX = random->nextSScalar1() * 1000.f;
3438 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003439 SkScalar scale;
3440 do {
3441 scale = random->nextSScalar1() * 100.f;
3442 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003443 SkMatrix viewMatrix;
3444 viewMatrix.setRotate(rotate);
3445 viewMatrix.postTranslate(translateX, translateY);
3446 viewMatrix.postScale(scale, scale);
3447 SkRect circle = GrTest::TestSquare(random);
3448 SkPoint center = {circle.centerX(), circle.centerY()};
3449 SkScalar radius = circle.width() / 2.f;
3450 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3451 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3452 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3453 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3454 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003455 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3456 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003457 startAngle, onAngle, offAngle, phase);
3458}
3459
Brian Salomon05441c42017-05-15 16:45:49 -04003460GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003461 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003462 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003463 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003464 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003465}
3466
Brian Salomon05441c42017-05-15 16:45:49 -04003467GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003468 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003469 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003470 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003471 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003472}
3473
Jim Van Verth64b85892019-06-17 12:01:46 -04003474GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3475 do {
3476 SkScalar rotate = random->nextSScalar1() * 360.f;
3477 SkScalar translateX = random->nextSScalar1() * 1000.f;
3478 SkScalar translateY = random->nextSScalar1() * 1000.f;
3479 SkScalar scale;
3480 do {
3481 scale = random->nextSScalar1() * 100.f;
3482 } while (scale == 0);
3483 SkMatrix viewMatrix;
3484 viewMatrix.setRotate(rotate);
3485 viewMatrix.postTranslate(translateX, translateY);
3486 viewMatrix.postScale(scale, scale);
3487 SkRect rect = GrTest::TestRect(random);
3488 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3489 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3490 if (rrect.isOval()) {
3491 continue;
3492 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003493 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003494 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3495 GrTest::TestStrokeRec(random), nullptr);
3496 if (op) {
3497 return op;
3498 }
3499 assert_alive(paint);
3500 } while (true);
3501}
3502
Brian Salomon05441c42017-05-15 16:45:49 -04003503GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003504 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003505 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003506 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003507 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003508}
3509
3510#endif