blob: 1dd179901c2459c7b7518ceec69558e6af45dd00 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
78 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
79 GLSLProcessor::GenKey(*this, caps, b);
80 }
81
Robert Phillipsf10535f2021-03-23 09:30:45 -040082 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050083 return new GLSLProcessor();
84 }
85
86private:
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400119 const CircleGeometryProcessor& cgp = args.fGeomProc.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500155 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500156 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800157
joshualittabb52a12015-01-13 15:02:10 -0800158 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400159 WriteOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
160 WriteLocalCoord(vertBuilder,
161 uniformHandler,
162 *args.fShaderCaps,
163 gpArgs,
164 cgp.fInPosition.asShaderVar(),
165 cgp.fLocalMatrix,
166 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800167
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400168 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500169 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000170 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800171 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500172 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500173 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000174 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800175 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000177
Brian Salomon92be2f72018-06-19 14:33:47 -0400178 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500179 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000180 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
181 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
185 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400187 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500188 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000189 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
190 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700191 }
192 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400193 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400194 // We compute coverage of the round caps as circles at the butt caps produced
195 // by the clip planes. The inverse of the clip planes is applied so that there
196 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400197 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500198 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
199 " roundCapCenters.xy)));"
200 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
201 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400202 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400203 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
204 capRadius.fsIn(), capRadius.fsIn());
205 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700206 }
John Stiles4d7ac492021-03-09 20:16:43 -0500207 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208 }
209
robertphillips46d36f02015-01-18 08:14:14 -0800210 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400211 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700212 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800213 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500214 b->addBool(cgp.fStroke, "stroked");
215 b->addBool(cgp.fInClipPlane.isInitialized(), "clipPlane");
216 b->addBool(cgp.fInIsectPlane.isInitialized(), "isectPlane");
217 b->addBool(cgp.fInUnionPlane.isInitialized(), "unionPlane");
218 b->addBool(cgp.fInRoundCapCenters.isInitialized(), "roundCapCenters");
Brian Salomon5a328282021-04-14 10:32:25 -0400219 b->addBits(kMatrixKeyBits,
220 ComputeMatrixKey(shaderCaps, cgp.fLocalMatrix),
221 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222 }
223
Brian Osman609f1592020-07-01 15:14:39 -0400224 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400225 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400226 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400227 SetTransform(pdman,
228 shaderCaps,
229 fLocalMatrixUniform,
230 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
231 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700232 }
233
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400235 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400236
237 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
238 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239 };
240
Brian Salomon289e3d82016-12-14 15:52:56 -0500241 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400242
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500243 Attribute fInPosition;
244 Attribute fInColor;
245 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400246 // Optional attributes.
247 Attribute fInClipPlane;
248 Attribute fInIsectPlane;
249 Attribute fInUnionPlane;
250 Attribute fInRoundCapCenters;
251
Brian Salomon289e3d82016-12-14 15:52:56 -0500252 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400253 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254
John Stiles7571f9e2020-09-02 22:42:33 -0400255 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256};
257
bsalomoncdaa97b2016-03-08 08:30:14 -0800258GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000259
Hal Canary6f6961e2017-01-31 13:50:44 -0500260#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400262 bool stroke = d->fRandom->nextBool();
263 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500264 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400265 bool clipPlane = d->fRandom->nextBool();
266 bool isectPlane = d->fRandom->nextBool();
267 bool unionPlane = d->fRandom->nextBool();
268 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500269 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
270 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271}
Hal Canary6f6961e2017-01-31 13:50:44 -0500272#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
275public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500276 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
277 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600278 return arena->make([&](void* ptr) {
279 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
280 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400281 }
282
283 ~ButtCapDashedCircleGeometryProcessor() override {}
284
285 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
286
287 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
288 GLSLProcessor::GenKey(*this, caps, b);
289 }
290
Robert Phillipsf10535f2021-03-23 09:30:45 -0400291 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400292 return new GLSLProcessor();
293 }
294
295private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500296 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
297 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
298 , fLocalMatrix(localMatrix) {
299 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
300 fInColor = MakeColorAttribute("inColor", wideColor);
301 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
302 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
303 this->setVertexAttributes(&fInPosition, 4);
304 }
305
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400306 class GLSLProcessor : public GrGLSLGeometryProcessor {
307 public:
308 GLSLProcessor() {}
309
310 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
311 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400312 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400313 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
314 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
315 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
316 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
317
318 // emit attributes
319 varyingHandler->emitAttributes(bcscgp);
320 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500321 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400322
323 fragBuilder->codeAppend("float4 dashParams;");
324 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
327 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
328 varyingHandler->addVarying("wrapDashes", &wrapDashes,
329 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
330 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
331 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
332 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500333 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400334 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
335 // x = length of on interval, y = length of on + off.
336 // There are two other parameters in dashParams.zw:
337 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
338 // Each interval has a "corresponding" dash which may be shifted partially or
339 // fully out of its interval by the phase. So there may be up to two "visual"
340 // dashes in an interval.
341 // When computing coverage in an interval we look at three dashes. These are the
342 // "corresponding" dashes from the current, previous, and next intervals. Any of these
343 // may be phase shifted into our interval or even when phase=0 they may be within half a
344 // pixel distance of a pixel center in the interval.
345 // When in the first interval we need to check the dash from the last interval. And
346 // similarly when in the last interval we need to check the dash from the first
347 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
348 // We compute the dash begin/end angles in the vertex shader and apply them in the
349 // fragment shader when we detect we're in the first/last interval.
350 vertBuilder->codeAppend(R"(
351 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
352 // to the fragment shader as a varying.
353 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 // We can happen to be perfectly divisible.
356 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 }
359 // Let 'l' be the last interval before reaching 2 pi.
360 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
361 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
362 // interval.
363 half offset = 0;
364 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500365 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400366 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500367 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400368 }
369 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
370 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
371 // min.
372 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
373
374 // Based on the phase determine whether the -1st, 0th, or 1st interval's
375 // "corresponding" dash appears in the 0th interval and is closest to l.
376 offset = 0;
377 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500378 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400379 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 }
382 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
383 wrapDashes.w = wrapDashes.z + dashParams.x;
384 // The start of the dash we're considering may be clipped by the start of the
385 // circle.
386 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
387 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500388 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
390 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
391 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
392
393 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500394 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500396 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400397 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
398
399 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400400 WriteOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
401 WriteLocalCoord(vertBuilder,
402 uniformHandler,
403 *args.fShaderCaps,
404 gpArgs,
405 bcscgp.fInPosition.asShaderVar(),
406 bcscgp.fLocalMatrix,
407 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400409 GrShaderVar fnArgs[] = {
410 GrShaderVar("angleToEdge", kFloat_GrSLType),
411 GrShaderVar("diameter", kFloat_GrSLType),
412 };
John Stiles6b58a332020-10-26 17:53:06 -0400413 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
414 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400415 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 float linearDist;
417 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
418 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400419 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400420 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 fragBuilder->codeAppend(R"(
422 float d = length(circleEdge.xy) * circleEdge.z;
423
424 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400426 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500427 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400428 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400429 edgeAlpha *= innerAlpha;
430
Ethan Nicholase1f55022019-02-05 17:17:40 -0500431 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 angleFromStart = mod(angleFromStart, 6.28318530718);
433 float x = mod(angleFromStart, dashParams.y);
434 // Convert the radial distance from center to pixel into a diameter.
435 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
437 half(dashParams.w));
438 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
439 half(dashParams.y) + half(dashParams.x) -
440 half(dashParams.w));
441 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
442 half(-dashParams.y) + half(dashParams.x) -
443 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400444 half dashAlpha = 0;
445 )");
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.y = min(currDash.y, lastIntervalLength);
450 if (nextDash.x >= lastIntervalLength) {
451 // The next dash is outside the 0..2pi range, throw it away
452 nextDash.xy = half2(1000);
453 } else {
454 // Clip the end of the next dash to the end of the circle
455 nextDash.y = min(nextDash.y, lastIntervalLength);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
460 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400462 currDash.x = max(currDash.x, 0);
463 if (prevDash.y <= 0) {
464 // The previous dash is outside the 0..2pi range, throw it away
465 prevDash.xy = half2(1000);
466 } else {
467 // Clip the start previous dash to the start of the circle
468 prevDash.x = max(prevDash.x, 0);
469 }
470 }
471 )", fnName.c_str(), fnName.c_str());
472 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500473 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
474 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
475 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 dashAlpha = min(dashAlpha, 1);
477 edgeAlpha *= dashAlpha;
478 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
479 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500480 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400481 }
482
483 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400484 const GrShaderCaps& shaderCaps,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 GrProcessorKeyBuilder* b) {
486 const ButtCapDashedCircleGeometryProcessor& bcscgp =
487 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400488 b->addBits(kMatrixKeyBits,
489 ComputeMatrixKey(shaderCaps, bcscgp.fLocalMatrix),
490 "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 }
492
Brian Osman609f1592020-07-01 15:14:39 -0400493 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400494 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400495 const GrGeometryProcessor& geomProc) override {
Brian Salomon5a328282021-04-14 10:32:25 -0400496 SetTransform(pdman,
497 shaderCaps,
498 fLocalMatrixUniform,
499 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
500 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501 }
502
503 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400504 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400505
506 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
507 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 };
509
510 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500511 Attribute fInPosition;
512 Attribute fInColor;
513 Attribute fInCircleEdge;
514 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400515
516 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
517
John Stiles7571f9e2020-09-02 22:42:33 -0400518 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400519};
520
521#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500522GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500523 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400524 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400526}
527#endif
528
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000529///////////////////////////////////////////////////////////////////////////////
530
531/**
532 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000533 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
534 * in both x and y directions.
535 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000536 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000537 */
538
bsalomoncdaa97b2016-03-08 08:30:14 -0800539class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000540public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500541 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
542 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600543 return arena->make([&](void* ptr) {
544 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
545 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500546 }
547
548 ~EllipseGeometryProcessor() override {}
549
550 const char* name() const override { return "EllipseGeometryProcessor"; }
551
552 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
553 GLSLProcessor::GenKey(*this, caps, b);
554 }
555
Robert Phillipsf10535f2021-03-23 09:30:45 -0400556 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500557 return new GLSLProcessor();
558 }
559
560private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000561 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400562 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500563 : INHERITED(kEllipseGeometryProcessor_ClassID)
564 , fLocalMatrix(localMatrix)
565 , fStroke(stroke)
566 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500567 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500568 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400569 if (useScale) {
570 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
571 } else {
572 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
573 }
574 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500575 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576 }
577
egdaniel57d3b032015-11-13 11:57:27 -0800578 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800580 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000581
Brian Salomon289e3d82016-12-14 15:52:56 -0500582 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400583 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800584 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800585 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800586 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000587
joshualittabb52a12015-01-13 15:02:10 -0800588 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800589 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800590
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400591 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
592 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800593 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800594 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500595 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000596
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400597 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800598 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500599 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800600
Chris Dalton60283612018-02-14 13:38:14 -0700601 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700602 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500603 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500604 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800605
joshualittabb52a12015-01-13 15:02:10 -0800606 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400607 WriteOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
608 WriteLocalCoord(vertBuilder,
609 uniformHandler,
610 *args.fShaderCaps,
611 gpArgs,
612 egp.fInPosition.asShaderVar(),
613 egp.fLocalMatrix,
614 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800615
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400616 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
617 // to compute both the edges because we need two separate test equations for
618 // the single offset.
619 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
620 // the distance by the gradient, non-uniformly scaled by the inverse of the
621 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800622
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400623 // On medium precision devices, we scale the denominator of the distance equation
624 // before taking the inverse square root to minimize the chance that we're dividing
625 // by zero, then we scale the result back.
626
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000627 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400628 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400629 if (egp.fStroke) {
630 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
631 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400632 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
633 if (egp.fUseScale) {
634 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
635 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
636 } else {
637 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
638 }
639 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700640
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000641 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400642 if (args.fShaderCaps->floatIs32Bits()) {
643 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
644 } else {
645 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
646 }
647 if (egp.fUseScale) {
648 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
649 ellipseOffsets.fsIn());
650 } else {
651 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
652 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000653 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000655 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800656 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400657 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800658 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400659 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400660 if (egp.fUseScale) {
661 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
662 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
663 } else {
664 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
665 }
666 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
667 if (!args.fShaderCaps->floatIs32Bits()) {
668 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
669 }
670 if (egp.fUseScale) {
671 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
672 ellipseOffsets.fsIn());
673 } else {
674 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
675 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000676 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677 }
678
John Stiles4d7ac492021-03-09 20:16:43 -0500679 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 }
681
robertphillips46d36f02015-01-18 08:14:14 -0800682 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400683 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700684 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800685 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500686 b->addBool(egp.fStroke, "stroked");
Brian Salomon5a328282021-04-14 10:32:25 -0400687 b->addBits(kMatrixKeyBits,
688 ComputeMatrixKey(shaderCaps, egp.fLocalMatrix),
689 "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690 }
691
Brian Osman609f1592020-07-01 15:14:39 -0400692 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400693 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400694 const GrGeometryProcessor& geomProc) override {
695 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
Brian Salomon5a328282021-04-14 10:32:25 -0400696 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700697 }
698
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400700 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400701
702 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
703 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704 };
705
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500706 Attribute fInPosition;
707 Attribute fInColor;
708 Attribute fInEllipseOffset;
709 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400710
joshualitte3ababe2015-05-15 07:56:07 -0700711 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000712 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400713 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000714
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400715 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000716
John Stiles7571f9e2020-09-02 22:42:33 -0400717 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000718};
719
bsalomoncdaa97b2016-03-08 08:30:14 -0800720GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000721
Hal Canary6f6961e2017-01-31 13:50:44 -0500722#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500723GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
724 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
725 d->fRandom->nextBool(), d->fRandom->nextBool(),
726 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000727}
Hal Canary6f6961e2017-01-31 13:50:44 -0500728#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000729
730///////////////////////////////////////////////////////////////////////////////
731
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000732/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000733 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000734 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
735 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
736 * using differentials.
737 *
738 * The result is device-independent and can be used with any affine matrix.
739 */
740
bsalomoncdaa97b2016-03-08 08:30:14 -0800741enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000742
bsalomoncdaa97b2016-03-08 08:30:14 -0800743class DIEllipseGeometryProcessor : public GrGeometryProcessor {
744public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500745 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
746 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600747 return arena->make([&](void* ptr) {
748 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
749 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500750 }
751
752 ~DIEllipseGeometryProcessor() override {}
753
754 const char* name() const override { return "DIEllipseGeometryProcessor"; }
755
756 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
757 GLSLProcessor::GenKey(*this, caps, b);
758 }
759
Robert Phillipsf10535f2021-03-23 09:30:45 -0400760 GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const override {
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500761 return new GLSLProcessor();
762 }
763
764private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000765 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400766 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400767 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400768 , fViewMatrix(viewMatrix)
769 , fUseScale(useScale)
770 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500771 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500772 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400773 if (useScale) {
774 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
775 kFloat3_GrSLType};
776 } else {
777 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
778 kFloat2_GrSLType};
779 }
780 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500781 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000782 }
783
egdaniel57d3b032015-11-13 11:57:27 -0800784 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000785 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500786 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000787
joshualitt465283c2015-09-11 08:19:35 -0700788 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400789 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800790 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800791 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800792 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793
joshualittabb52a12015-01-13 15:02:10 -0800794 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800795 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800796
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400797 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
798 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800799 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500800 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700801
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400802 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800803 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500804 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800805
Chris Dalton60283612018-02-14 13:38:14 -0700806 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500807 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500808 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800809
joshualittabb52a12015-01-13 15:02:10 -0800810 // Setup position
Brian Salomon5a328282021-04-14 10:32:25 -0400811 WriteOutputPosition(vertBuilder,
812 uniformHandler,
813 *args.fShaderCaps,
814 gpArgs,
815 diegp.fInPosition.name(),
816 diegp.fViewMatrix,
817 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400818 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800819
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000820 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400821 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
822 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
823 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
824 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500825 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400826 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
827 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500828 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400829 if (diegp.fUseScale) {
830 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
831 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000832
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000834 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 if (args.fShaderCaps->floatIs32Bits()) {
836 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
837 } else {
838 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
839 }
840 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
841 if (diegp.fUseScale) {
842 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
843 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800844 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000845 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000846 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
847 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000848 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000849 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 }
851
852 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800853 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800854 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
855 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400856 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
857 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500858 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400859 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
860 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500861 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400862 if (diegp.fUseScale) {
863 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
864 }
865 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
866 if (!args.fShaderCaps->floatIs32Bits()) {
867 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
868 }
869 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
870 if (diegp.fUseScale) {
871 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
872 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000873 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000874 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000875
John Stiles4d7ac492021-03-09 20:16:43 -0500876 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000877 }
878
robertphillips46d36f02015-01-18 08:14:14 -0800879 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon5a328282021-04-14 10:32:25 -0400880 const GrShaderCaps& shaderCaps,
joshualittb0a8a372014-09-23 09:50:21 -0700881 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800882 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500883 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
Brian Salomon5a328282021-04-14 10:32:25 -0400884 b->addBits(kMatrixKeyBits,
885 ComputeMatrixKey(shaderCaps, diegp.fViewMatrix),
886 "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000887 }
888
Brian Osman609f1592020-07-01 15:14:39 -0400889 void setData(const GrGLSLProgramDataManager& pdman,
Brian Salomon5a328282021-04-14 10:32:25 -0400890 const GrShaderCaps& shaderCaps,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400891 const GrGeometryProcessor& geomProc) override {
892 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700893
Brian Salomon5a328282021-04-14 10:32:25 -0400894 SetTransform(pdman, shaderCaps, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000895 }
896
897 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400898 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700899 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800900
John Stiles7571f9e2020-09-02 22:42:33 -0400901 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000902 };
903
Brian Salomon92be2f72018-06-19 14:33:47 -0400904
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500905 Attribute fInPosition;
906 Attribute fInColor;
907 Attribute fInEllipseOffsets0;
908 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400909
Brian Salomon289e3d82016-12-14 15:52:56 -0500910 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400911 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500912 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000913
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400914 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915
John Stiles7571f9e2020-09-02 22:42:33 -0400916 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000917};
918
bsalomoncdaa97b2016-03-08 08:30:14 -0800919GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000920
Hal Canary6f6961e2017-01-31 13:50:44 -0500921#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500922GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
923 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
924 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
925 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000926}
Hal Canary6f6961e2017-01-31 13:50:44 -0500927#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000928
929///////////////////////////////////////////////////////////////////////////////
930
jvanverth6ca48822016-10-07 06:57:32 -0700931// We have two possible cases for geometry for a circle:
932
933// In the case of a normal fill, we draw geometry for the circle as an octagon.
934static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500935 // enter the octagon
936 // clang-format off
937 0, 1, 8, 1, 2, 8,
938 2, 3, 8, 3, 4, 8,
939 4, 5, 8, 5, 6, 8,
940 6, 7, 8, 7, 0, 8
941 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700942};
943
944// For stroked circles, we use two nested octagons.
945static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500946 // enter the octagon
947 // clang-format off
948 0, 1, 9, 0, 9, 8,
949 1, 2, 10, 1, 10, 9,
950 2, 3, 11, 2, 11, 10,
951 3, 4, 12, 3, 12, 11,
952 4, 5, 13, 4, 13, 12,
953 5, 6, 14, 5, 14, 13,
954 6, 7, 15, 6, 15, 14,
955 7, 0, 8, 7, 8, 15,
956 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700957};
958
Brian Osman9d958b52018-11-13 12:46:56 -0500959// Normalized geometry for octagons that circumscribe and lie on a circle:
960
961static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
962static constexpr SkPoint kOctagonOuter[] = {
963 SkPoint::Make(-kOctOffset, -1),
964 SkPoint::Make( kOctOffset, -1),
965 SkPoint::Make( 1, -kOctOffset),
966 SkPoint::Make( 1, kOctOffset),
967 SkPoint::Make( kOctOffset, 1),
968 SkPoint::Make(-kOctOffset, 1),
969 SkPoint::Make(-1, kOctOffset),
970 SkPoint::Make(-1, -kOctOffset),
971};
972
973// cosine and sine of pi/8
974static constexpr SkScalar kCosPi8 = 0.923579533f;
975static constexpr SkScalar kSinPi8 = 0.382683432f;
976static constexpr SkPoint kOctagonInner[] = {
977 SkPoint::Make(-kSinPi8, -kCosPi8),
978 SkPoint::Make( kSinPi8, -kCosPi8),
979 SkPoint::Make( kCosPi8, -kSinPi8),
980 SkPoint::Make( kCosPi8, kSinPi8),
981 SkPoint::Make( kSinPi8, kCosPi8),
982 SkPoint::Make(-kSinPi8, kCosPi8),
983 SkPoint::Make(-kCosPi8, kSinPi8),
984 SkPoint::Make(-kCosPi8, -kSinPi8),
985};
Brian Salomon289e3d82016-12-14 15:52:56 -0500986
jvanverth6ca48822016-10-07 06:57:32 -0700987static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
988static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
989static const int kVertsPerStrokeCircle = 16;
990static const int kVertsPerFillCircle = 9;
991
992static int circle_type_to_vert_count(bool stroked) {
993 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
994}
995
996static int circle_type_to_index_count(bool stroked) {
997 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
998}
999
1000static const uint16_t* circle_type_to_indices(bool stroked) {
1001 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1002}
1003
1004///////////////////////////////////////////////////////////////////////////////
1005
Brian Salomon05441c42017-05-15 16:45:49 -04001006class CircleOp final : public GrMeshDrawOp {
1007private:
1008 using Helper = GrSimpleMeshDrawOpHelper;
1009
joshualitt76e7fb62015-02-11 08:52:27 -08001010public:
Brian Salomon25a88092016-12-01 09:36:50 -05001011 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001012
bsalomon4f3a0ca2016-08-22 13:14:26 -07001013 /** Optional extra params to render a partial arc rather than a full circle. */
1014 struct ArcParams {
1015 SkScalar fStartAngleRadians;
1016 SkScalar fSweepAngleRadians;
1017 bool fUseCenter;
1018 };
Brian Salomon05441c42017-05-15 16:45:49 -04001019
Herb Derbyc76d4092020-10-07 16:46:15 -04001020 static GrOp::Owner Make(GrRecordingContext* context,
1021 GrPaint&& paint,
1022 const SkMatrix& viewMatrix,
1023 SkPoint center,
1024 SkScalar radius,
1025 const GrStyle& style,
1026 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001027 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001028 if (style.hasPathEffect()) {
1029 return nullptr;
1030 }
Brian Salomon05441c42017-05-15 16:45:49 -04001031 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001032 SkStrokeRec::Style recStyle = stroke.getStyle();
1033 if (arcParams) {
1034 // Arc support depends on the style.
1035 switch (recStyle) {
1036 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001037 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001038 return nullptr;
1039 case SkStrokeRec::kFill_Style:
1040 // This supports all fills.
1041 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001042 case SkStrokeRec::kStroke_Style:
1043 // Strokes that don't use the center point are supported with butt and round
1044 // caps.
1045 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1046 return nullptr;
1047 }
1048 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001050 // Hairline only supports butt cap. Round caps could be emulated by slightly
1051 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001052 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1053 return nullptr;
1054 }
1055 break;
1056 }
1057 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001058 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1059 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001060 }
1061
Herb Derbyc76d4092020-10-07 16:46:15 -04001062 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001063 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1064 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001065 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001066 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001067 const SkStrokeRec& stroke = style.strokeRec();
1068 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001069
Brian Salomon45c92202018-04-10 10:53:58 -04001070 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001071
bsalomon4b4a7cc2016-07-08 04:42:54 -07001072 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001074 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001075
Brian Salomon289e3d82016-12-14 15:52:56 -05001076 bool isStrokeOnly =
1077 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001079
jvanverth6ca48822016-10-07 06:57:32 -07001080 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001081 SkScalar outerRadius = radius;
1082 SkScalar halfWidth = 0;
1083 if (hasStroke) {
1084 if (SkScalarNearlyZero(strokeWidth)) {
1085 halfWidth = SK_ScalarHalf;
1086 } else {
1087 halfWidth = SkScalarHalf(strokeWidth);
1088 }
1089
1090 outerRadius += halfWidth;
1091 if (isStrokeOnly) {
1092 innerRadius = radius - halfWidth;
1093 }
1094 }
1095
1096 // The radii are outset for two reasons. First, it allows the shader to simply perform
1097 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1098 // Second, the outer radius is used to compute the verts of the bounding box that is
1099 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001100 outerRadius += SK_ScalarHalf;
1101 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001102 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001103 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001104
bsalomon4f3a0ca2016-08-22 13:14:26 -07001105 // This makes every point fully inside the intersection plane.
1106 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1107 // This makes every point fully outside the union plane.
1108 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001109 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1111 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001112 if (arcParams) {
1113 // The shader operates in a space where the circle is translated to be centered at the
1114 // origin. Here we compute points on the unit circle at the starting and ending angles.
1115 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001116 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1117 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001119 stopPoint.fY = SkScalarSin(endAngle);
1120 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001121
1122 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1123 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1124 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1125 startPoint.normalize();
1126 stopPoint.normalize();
1127
Brian Salomon3517aa72019-12-11 08:16:22 -05001128 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1129 // should orient the clip planes for arcs.
1130 SkASSERT(viewMatrix.isSimilarity());
1131 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1132 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1133 if (upperLeftDet < 0) {
1134 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001135 }
1136
Brian Salomon45c92202018-04-10 10:53:58 -04001137 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1138 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1139 SkPoint roundCaps[2];
1140 if (fRoundCaps) {
1141 // Compute the cap center points in the normalized space.
1142 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1143 roundCaps[0] = startPoint * midRadius;
1144 roundCaps[1] = stopPoint * midRadius;
1145 } else {
1146 roundCaps[0] = kUnusedRoundCaps[0];
1147 roundCaps[1] = kUnusedRoundCaps[1];
1148 }
1149
bsalomon4f3a0ca2016-08-22 13:14:26 -07001150 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001151 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1152 // center of the butts.
1153 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001155 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001156 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001157 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1158 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1159 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001160 if (useCenter) {
1161 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1162 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001163 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1164 if (arcParams->fSweepAngleRadians < 0) {
1165 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001166 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001167 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001168 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001169 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001170 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001171 color,
1172 innerRadius,
1173 outerRadius,
1174 {norm0.fX, norm0.fY, 0.5f},
1175 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1176 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001177 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001178 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001179 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001180 fClipPlaneIsect = false;
1181 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001182 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001183 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001184 color,
1185 innerRadius,
1186 outerRadius,
1187 {norm0.fX, norm0.fY, 0.5f},
1188 {norm1.fX, norm1.fY, 0.5f},
1189 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001190 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001191 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001192 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fClipPlaneIsect = true;
1194 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 }
1196 } else {
1197 // We clip to a secant of the original circle.
1198 startPoint.scale(radius);
1199 stopPoint.scale(radius);
1200 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1201 norm.normalize();
1202 if (arcParams->fSweepAngleRadians > 0) {
1203 norm.negate();
1204 }
1205 SkScalar d = -norm.dot(startPoint) + 0.5f;
1206
Brian Salomon05441c42017-05-15 16:45:49 -04001207 fCircles.emplace_back(
1208 Circle{color,
1209 innerRadius,
1210 outerRadius,
1211 {norm.fX, norm.fY, d},
1212 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1213 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001214 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001215 devBounds,
1216 stroked});
1217 fClipPlane = true;
1218 fClipPlaneIsect = false;
1219 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001220 }
1221 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001222 fCircles.emplace_back(
1223 Circle{color,
1224 innerRadius,
1225 outerRadius,
1226 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1227 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1228 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001229 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001230 devBounds,
1231 stroked});
1232 fClipPlane = false;
1233 fClipPlaneIsect = false;
1234 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001235 }
bsalomon88cf17d2016-07-08 06:40:56 -07001236 // Use the original radius and stroke radius for the bounds so that it does not include the
1237 // AA bloat.
1238 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001239 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001240 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001241 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001242 fVertCount = circle_type_to_vert_count(stroked);
1243 fIndexCount = circle_type_to_index_count(stroked);
1244 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001245 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001246
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001248
Chris Dalton1706cbf2019-05-21 19:35:29 -06001249 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001250 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001251 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001252 } else {
1253 fHelper.visitProxies(func);
1254 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001255 }
1256
Chris Dalton6ce447a2019-06-23 18:07:38 -06001257 GrProcessorSet::Analysis finalize(
1258 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1259 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001260 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001261 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001262 GrProcessorAnalysisCoverage::kSingleChannel, color,
1263 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001264 }
1265
1266 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1267
bsalomone46f9fe2015-08-18 06:05:14 -07001268private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001269 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001270
Robert Phillips4133dc42020-03-11 15:55:55 -04001271 void onCreateProgramInfo(const GrCaps* caps,
1272 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001273 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001274 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001275 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001276 GrXferBarrierFlags renderPassXferBarriers,
1277 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001278 SkMatrix localMatrix;
1279 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001280 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001281 }
1282
Robert Phillips4490d922020-03-03 14:50:59 -05001283 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001284 fClipPlaneIsect, fClipPlaneUnion,
1285 fRoundCaps, fWideColor,
1286 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001287
Brian Salomon8afde5f2020-04-01 16:22:00 -04001288 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001289 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001290 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001291 }
1292
Robert Phillips4490d922020-03-03 14:50:59 -05001293 void onPrepareDraws(Target* target) override {
1294 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001295 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001296 if (!fProgramInfo) {
1297 return;
1298 }
1299 }
1300
Brian Salomon12d22642019-01-29 14:38:50 -05001301 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001302 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001303 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001304 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001305 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001306 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001307 return;
1308 }
1309
Brian Salomon12d22642019-01-29 14:38:50 -05001310 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001311 int firstIndex = 0;
1312 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1313 if (!indices) {
1314 SkDebugf("Could not allocate indices\n");
1315 return;
1316 }
1317
1318 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001319 for (const auto& circle : fCircles) {
1320 SkScalar innerRadius = circle.fInnerRadius;
1321 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001322 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001323 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001324
joshualitt76e7fb62015-02-11 08:52:27 -08001325 // The inner radius in the vertex data must be specified in normalized space.
1326 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001327 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001328
1329 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001330 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001331
Brian Osman9a24fee2018-08-03 09:48:42 -04001332 SkVector geoClipPlane = { 0, 0 };
1333 SkScalar offsetClipDist = SK_Scalar1;
1334 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1335 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1336 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1337 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1338 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1339 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1340 // the AA can extend just past the center of the circle.
1341 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1342 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1343 SkAssertResult(geoClipPlane.normalize());
1344 offsetClipDist = 0.5f / halfWidth;
1345 }
1346
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 for (int i = 0; i < 8; ++i) {
1348 // This clips the normalized offset to the half-plane we computed above. Then we
1349 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001350 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001351 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(center + offset * halfWidth,
1353 color,
1354 offset,
1355 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001357 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001358 }
1359 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001360 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001361 }
1362 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001364 }
1365 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001366 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001367 }
Brian Salomon45c92202018-04-10 10:53:58 -04001368 }
jvanverth6ca48822016-10-07 06:57:32 -07001369
Brian Salomon05441c42017-05-15 16:45:49 -04001370 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001371 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001372
Brian Osman7d8f82b2018-11-08 10:24:09 -05001373 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001374 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1375 color,
1376 kOctagonInner[i] * innerRadius,
1377 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001378 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001379 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001380 }
1381 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001382 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001383 }
1384 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001385 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001386 }
1387 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001388 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001389 }
Brian Salomon45c92202018-04-10 10:53:58 -04001390 }
jvanverth6ca48822016-10-07 06:57:32 -07001391 } else {
1392 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001393 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001394 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001395 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001396 }
jvanverth6ca48822016-10-07 06:57:32 -07001397 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001398 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001399 }
1400 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001401 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001402 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001403 if (fRoundCaps) {
1404 vertices.write(circle.fRoundCapCenters);
1405 }
jvanverth6ca48822016-10-07 06:57:32 -07001406 }
1407
Brian Salomon05441c42017-05-15 16:45:49 -04001408 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1409 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001410 for (int i = 0; i < primIndexCount; ++i) {
1411 *indices++ = primIndices[i] + currStartVertex;
1412 }
1413
Brian Salomon05441c42017-05-15 16:45:49 -04001414 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001415 }
jvanverth6ca48822016-10-07 06:57:32 -07001416
Robert Phillips4490d922020-03-03 14:50:59 -05001417 fMesh = target->allocMesh();
1418 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001419 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001420 }
1421
1422 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001423 if (!fProgramInfo || !fMesh) {
1424 return;
1425 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001426
Chris Dalton765ed362020-03-16 17:34:44 -06001427 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001428 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001429 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001430 }
1431
Herb Derbye25c3002020-10-27 15:57:27 -04001432 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001433 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001434
1435 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001436 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001437 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001438 }
1439
Brian Salomon05441c42017-05-15 16:45:49 -04001440 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001441 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001442 }
1443
Brian Salomon05441c42017-05-15 16:45:49 -04001444 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001445 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1446 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001447 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001448 }
1449
Brian Salomon289e3d82016-12-14 15:52:56 -05001450 // Because we've set up the ops that don't use the planes with noop values
1451 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001452 fClipPlane |= that->fClipPlane;
1453 fClipPlaneIsect |= that->fClipPlaneIsect;
1454 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001455 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001456 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001457
Brian Salomon05441c42017-05-15 16:45:49 -04001458 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001459 fVertCount += that->fVertCount;
1460 fIndexCount += that->fIndexCount;
1461 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001462 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001463 }
1464
John Stilesaf366522020-08-13 09:57:34 -04001465#if GR_TEST_UTILS
1466 SkString onDumpInfo() const override {
1467 SkString string;
1468 for (int i = 0; i < fCircles.count(); ++i) {
1469 string.appendf(
1470 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1471 "InnerRad: %.2f, OuterRad: %.2f\n",
1472 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1473 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1474 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1475 fCircles[i].fOuterRadius);
1476 }
1477 string += fHelper.dumpInfo();
1478 return string;
1479 }
1480#endif
1481
Brian Salomon05441c42017-05-15 16:45:49 -04001482 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001483 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001484 SkScalar fInnerRadius;
1485 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001486 SkScalar fClipPlane[3];
1487 SkScalar fIsectPlane[3];
1488 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001489 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001490 SkRect fDevBounds;
1491 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001492 };
1493
Brian Salomon289e3d82016-12-14 15:52:56 -05001494 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001495 Helper fHelper;
1496 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001497 int fVertCount;
1498 int fIndexCount;
1499 bool fAllFill;
1500 bool fClipPlane;
1501 bool fClipPlaneIsect;
1502 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001503 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001504 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001505
Chris Daltoneb694b72020-03-16 09:25:50 -06001506 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001507 GrProgramInfo* fProgramInfo = nullptr;
1508
John Stiles7571f9e2020-09-02 22:42:33 -04001509 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001510};
1511
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001512class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1513private:
1514 using Helper = GrSimpleMeshDrawOpHelper;
1515
1516public:
1517 DEFINE_OP_CLASS_ID
1518
Herb Derbyc76d4092020-10-07 16:46:15 -04001519 static GrOp::Owner Make(GrRecordingContext* context,
1520 GrPaint&& paint,
1521 const SkMatrix& viewMatrix,
1522 SkPoint center,
1523 SkScalar radius,
1524 SkScalar strokeWidth,
1525 SkScalar startAngle,
1526 SkScalar onAngle,
1527 SkScalar offAngle,
1528 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001529 SkASSERT(circle_stays_circle(viewMatrix));
1530 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001531 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1532 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001533 onAngle, offAngle, phaseAngle);
1534 }
1535
Herb Derbyc76d4092020-10-07 16:46:15 -04001536 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001537 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1538 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1539 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001540 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001541 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001542 SkASSERT(circle_stays_circle(viewMatrix));
1543 viewMatrix.mapPoints(&center, 1);
1544 radius = viewMatrix.mapRadius(radius);
1545 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1546
1547 // Determine the angle where the circle starts in device space and whether its orientation
1548 // has been reversed.
1549 SkVector start;
1550 bool reflection;
1551 if (!startAngle) {
1552 start = {1, 0};
1553 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001554 start.fY = SkScalarSin(startAngle);
1555 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001556 }
1557 viewMatrix.mapVectors(&start, 1);
1558 startAngle = SkScalarATan2(start.fY, start.fX);
1559 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1560 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1561
1562 auto totalAngle = onAngle + offAngle;
1563 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1564
1565 SkScalar halfWidth = 0;
1566 if (SkScalarNearlyZero(strokeWidth)) {
1567 halfWidth = SK_ScalarHalf;
1568 } else {
1569 halfWidth = SkScalarHalf(strokeWidth);
1570 }
1571
1572 SkScalar outerRadius = radius + halfWidth;
1573 SkScalar innerRadius = radius - halfWidth;
1574
1575 // The radii are outset for two reasons. First, it allows the shader to simply perform
1576 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1577 // Second, the outer radius is used to compute the verts of the bounding box that is
1578 // rendered and the outset ensures the box will cover all partially covered by the circle.
1579 outerRadius += SK_ScalarHalf;
1580 innerRadius -= SK_ScalarHalf;
1581 fViewMatrixIfUsingLocalCoords = viewMatrix;
1582
1583 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1584 center.fX + outerRadius, center.fY + outerRadius);
1585
1586 // We store whether there is a reflection as a negative total angle.
1587 if (reflection) {
1588 totalAngle = -totalAngle;
1589 }
1590 fCircles.push_back(Circle{
1591 color,
1592 outerRadius,
1593 innerRadius,
1594 onAngle,
1595 totalAngle,
1596 startAngle,
1597 phaseAngle,
1598 devBounds
1599 });
1600 // Use the original radius and stroke radius for the bounds so that it does not include the
1601 // AA bloat.
1602 radius += halfWidth;
1603 this->setBounds(
1604 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001605 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001606 fVertCount = circle_type_to_vert_count(true);
1607 fIndexCount = circle_type_to_index_count(true);
1608 }
1609
1610 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1611
Chris Dalton1706cbf2019-05-21 19:35:29 -06001612 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001613 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001614 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001615 } else {
1616 fHelper.visitProxies(func);
1617 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001618 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001619
Chris Dalton6ce447a2019-06-23 18:07:38 -06001620 GrProcessorSet::Analysis finalize(
1621 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1622 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001623 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001624 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001625 GrProcessorAnalysisCoverage::kSingleChannel, color,
1626 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001627 }
1628
1629 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1630
1631private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001632 GrProgramInfo* programInfo() override { return fProgramInfo; }
1633
Robert Phillips4133dc42020-03-11 15:55:55 -04001634 void onCreateProgramInfo(const GrCaps* caps,
1635 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001636 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001637 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001638 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001639 GrXferBarrierFlags renderPassXferBarriers,
1640 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001641 SkMatrix localMatrix;
1642 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001643 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001644 }
1645
1646 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001647 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001648 fWideColor,
1649 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001650
Brian Salomon8afde5f2020-04-01 16:22:00 -04001651 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001652 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001653 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001654 }
1655
Robert Phillips4490d922020-03-03 14:50:59 -05001656 void onPrepareDraws(Target* target) override {
1657 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001658 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001659 if (!fProgramInfo) {
1660 return;
1661 }
1662 }
1663
Brian Salomon12d22642019-01-29 14:38:50 -05001664 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001665 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001666 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001667 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001668 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001669 SkDebugf("Could not allocate vertices\n");
1670 return;
1671 }
1672
Brian Salomon12d22642019-01-29 14:38:50 -05001673 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001674 int firstIndex = 0;
1675 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1676 if (!indices) {
1677 SkDebugf("Could not allocate indices\n");
1678 return;
1679 }
1680
1681 int currStartVertex = 0;
1682 for (const auto& circle : fCircles) {
1683 // The inner radius in the vertex data must be specified in normalized space so that
1684 // length() can be called with smaller values to avoid precision issues with half
1685 // floats.
1686 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1687 const SkRect& bounds = circle.fDevBounds;
1688 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001689 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1690 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1691 };
1692 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001693 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001694 dashParams.totalAngle = -dashParams.totalAngle;
1695 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001696 }
1697
Brian Osmane3caf2d2018-11-21 13:48:36 -05001698 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001699
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700 // The bounding geometry for the circle is composed of an outer bounding octagon and
1701 // an inner bounded octagon.
1702
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001703 // Compute the vertices of the outer octagon.
1704 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1705 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001706
1707 auto reflectY = [=](const SkPoint& p) {
1708 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001709 };
Brian Osman9d958b52018-11-13 12:46:56 -05001710
1711 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001712 vertices.write(center + kOctagonOuter[i] * halfWidth,
1713 color,
1714 reflectY(kOctagonOuter[i]),
1715 circle.fOuterRadius,
1716 normInnerRadius,
1717 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001718 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001719
1720 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001721 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001722 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1723 color,
1724 reflectY(kOctagonInner[i]) * normInnerRadius,
1725 circle.fOuterRadius,
1726 normInnerRadius,
1727 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001728 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001729
1730 const uint16_t* primIndices = circle_type_to_indices(true);
1731 const int primIndexCount = circle_type_to_index_count(true);
1732 for (int i = 0; i < primIndexCount; ++i) {
1733 *indices++ = primIndices[i] + currStartVertex;
1734 }
1735
1736 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001737 }
1738
Robert Phillips4490d922020-03-03 14:50:59 -05001739 fMesh = target->allocMesh();
1740 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001741 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001742 }
1743
1744 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001745 if (!fProgramInfo || !fMesh) {
1746 return;
1747 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001748
Chris Dalton765ed362020-03-16 17:34:44 -06001749 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001750 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001751 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001752 }
1753
Herb Derbye25c3002020-10-27 15:57:27 -04001754 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001755 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1756
1757 // can only represent 65535 unique vertices with 16-bit indices
1758 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001759 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001760 }
1761
1762 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001763 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001764 }
1765
1766 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001767 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1768 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001769 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001770 }
1771
1772 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001773 fVertCount += that->fVertCount;
1774 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001775 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001776 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001777 }
1778
John Stilesaf366522020-08-13 09:57:34 -04001779#if GR_TEST_UTILS
1780 SkString onDumpInfo() const override {
1781 SkString string;
1782 for (int i = 0; i < fCircles.count(); ++i) {
1783 string.appendf(
1784 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1785 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1786 "Phase: %.2f\n",
1787 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1788 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1789 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1790 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1791 fCircles[i].fPhaseAngle);
1792 }
1793 string += fHelper.dumpInfo();
1794 return string;
1795 }
1796#endif
1797
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001798 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001799 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001800 SkScalar fOuterRadius;
1801 SkScalar fInnerRadius;
1802 SkScalar fOnAngle;
1803 SkScalar fTotalAngle;
1804 SkScalar fStartAngle;
1805 SkScalar fPhaseAngle;
1806 SkRect fDevBounds;
1807 };
1808
1809 SkMatrix fViewMatrixIfUsingLocalCoords;
1810 Helper fHelper;
1811 SkSTArray<1, Circle, true> fCircles;
1812 int fVertCount;
1813 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001814 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001815
Chris Daltoneb694b72020-03-16 09:25:50 -06001816 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001817 GrProgramInfo* fProgramInfo = nullptr;
1818
John Stiles7571f9e2020-09-02 22:42:33 -04001819 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001820};
1821
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001822///////////////////////////////////////////////////////////////////////////////
1823
Brian Salomon05441c42017-05-15 16:45:49 -04001824class EllipseOp : public GrMeshDrawOp {
1825private:
1826 using Helper = GrSimpleMeshDrawOpHelper;
1827
1828 struct DeviceSpaceParams {
1829 SkPoint fCenter;
1830 SkScalar fXRadius;
1831 SkScalar fYRadius;
1832 SkScalar fInnerXRadius;
1833 SkScalar fInnerYRadius;
1834 };
1835
joshualitt76e7fb62015-02-11 08:52:27 -08001836public:
Brian Salomon25a88092016-12-01 09:36:50 -05001837 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001838
Herb Derbyc76d4092020-10-07 16:46:15 -04001839 static GrOp::Owner Make(GrRecordingContext* context,
1840 GrPaint&& paint,
1841 const SkMatrix& viewMatrix,
1842 const SkRect& ellipse,
1843 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001844 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001845 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001846 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1847 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001848 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1849 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001850 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1851 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1852 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1853 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001854
bsalomon4b4a7cc2016-07-08 04:42:54 -07001855 // do (potentially) anisotropic mapping of stroke
1856 SkVector scaledStroke;
1857 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001858 scaledStroke.fX = SkScalarAbs(
1859 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1860 scaledStroke.fY = SkScalarAbs(
1861 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001862
1863 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001864 bool isStrokeOnly =
1865 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001866 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1867
Brian Salomon05441c42017-05-15 16:45:49 -04001868 params.fInnerXRadius = 0;
1869 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001870 if (hasStroke) {
1871 if (SkScalarNearlyZero(scaledStroke.length())) {
1872 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1873 } else {
1874 scaledStroke.scale(SK_ScalarHalf);
1875 }
1876
1877 // we only handle thick strokes for near-circular ellipses
1878 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001879 (0.5f * params.fXRadius > params.fYRadius ||
1880 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001881 return nullptr;
1882 }
1883
1884 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001885 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1886 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1887 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1888 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001889 return nullptr;
1890 }
1891
1892 // this is legit only if scale & translation (which should be the case at the moment)
1893 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001894 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1895 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001896 }
1897
Brian Salomon05441c42017-05-15 16:45:49 -04001898 params.fXRadius += scaledStroke.fX;
1899 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001900 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001901
1902 // For large ovals with low precision floats, we fall back to the path renderer.
1903 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1904 // minimum value to avoid divides by zero. With large ovals and low precision this
1905 // leads to blurring at the edge of the oval.
1906 const SkScalar kMaxOvalRadius = 16384;
1907 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1908 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1909 return nullptr;
1910 }
1911
Greg Daniel2655ede2019-04-10 00:49:28 +00001912 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001913 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001914 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001915
Herb Derbyc76d4092020-10-07 16:46:15 -04001916 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001917 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001918 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001919 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001920 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001921 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001922 SkStrokeRec::Style style = stroke.getStyle();
1923 bool isStrokeOnly =
1924 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001925
Brian Salomon05441c42017-05-15 16:45:49 -04001926 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1927 params.fInnerXRadius, params.fInnerYRadius,
1928 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1929 params.fCenter.fY - params.fYRadius,
1930 params.fCenter.fX + params.fXRadius,
1931 params.fCenter.fY + params.fYRadius)});
1932
Greg Daniel5faf4742019-10-01 15:14:44 -04001933 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001934
bsalomon4b4a7cc2016-07-08 04:42:54 -07001935 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001936 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001937
Brian Salomon05441c42017-05-15 16:45:49 -04001938 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1939 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001940 }
joshualitt76e7fb62015-02-11 08:52:27 -08001941
Brian Salomon289e3d82016-12-14 15:52:56 -05001942 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001943
Chris Dalton1706cbf2019-05-21 19:35:29 -06001944 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001945 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001946 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001947 } else {
1948 fHelper.visitProxies(func);
1949 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001950 }
1951
Chris Dalton6ce447a2019-06-23 18:07:38 -06001952 GrProcessorSet::Analysis finalize(
1953 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1954 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001955 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1956 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001957 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001958 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001959 GrProcessorAnalysisCoverage::kSingleChannel, color,
1960 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001961 }
1962
1963 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1964
bsalomone46f9fe2015-08-18 06:05:14 -07001965private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001966 GrProgramInfo* programInfo() override { return fProgramInfo; }
1967
Robert Phillips4133dc42020-03-11 15:55:55 -04001968 void onCreateProgramInfo(const GrCaps* caps,
1969 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001970 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001971 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001972 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001973 GrXferBarrierFlags renderPassXferBarriers,
1974 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001975 SkMatrix localMatrix;
1976 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001977 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001978 }
1979
Robert Phillips4490d922020-03-03 14:50:59 -05001980 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1981 fUseScale, localMatrix);
1982
Brian Salomon8afde5f2020-04-01 16:22:00 -04001983 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001984 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001985 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001986 }
1987
Robert Phillips4490d922020-03-03 14:50:59 -05001988 void onPrepareDraws(Target* target) override {
1989 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001990 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001991 if (!fProgramInfo) {
1992 return;
1993 }
1994 }
1995
Robert Phillips787fd9d2021-03-22 14:48:09 -04001996 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001997 GrVertexWriter verts{helper.vertices()};
1998 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001999 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08002000 return;
2001 }
2002
Brian Salomon05441c42017-05-15 16:45:49 -04002003 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002004 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002005 SkScalar xRadius = ellipse.fXRadius;
2006 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002007
2008 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002009 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2010 SkScalarInvert(xRadius),
2011 SkScalarInvert(yRadius),
2012 SkScalarInvert(ellipse.fInnerXRadius),
2013 SkScalarInvert(ellipse.fInnerYRadius)
2014 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002015 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2016 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002017
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002018 if (!fStroked) {
2019 // For filled ellipses we map a unit circle in the vertex attributes rather than
2020 // computing an ellipse and modifying that distance, so we normalize to 1
2021 xMaxOffset /= xRadius;
2022 yMaxOffset /= yRadius;
2023 }
2024
joshualitt76e7fb62015-02-11 08:52:27 -08002025 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002026 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2027 color,
2028 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002029 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002030 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002031 }
Robert Phillips4490d922020-03-03 14:50:59 -05002032 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002033 }
2034
2035 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002036 if (!fProgramInfo || !fMesh) {
2037 return;
2038 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002039
Chris Dalton765ed362020-03-16 17:34:44 -06002040 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002041 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002042 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002043 }
2044
Herb Derbye25c3002020-10-27 15:57:27 -04002045 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002046 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002047
Brian Salomon05441c42017-05-15 16:45:49 -04002048 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002049 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002050 }
2051
bsalomoncdaa97b2016-03-08 08:30:14 -08002052 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002053 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002054 }
2055
Brian Salomon05441c42017-05-15 16:45:49 -04002056 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002057 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2058 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002059 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002060 }
2061
Brian Salomon05441c42017-05-15 16:45:49 -04002062 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002063 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002064 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002065 }
2066
John Stilesaf366522020-08-13 09:57:34 -04002067#if GR_TEST_UTILS
2068 SkString onDumpInfo() const override {
2069 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2070 for (const auto& geo : fEllipses) {
2071 string.appendf(
2072 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2073 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2074 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2075 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2076 geo.fInnerXRadius, geo.fInnerYRadius);
2077 }
2078 string += fHelper.dumpInfo();
2079 return string;
2080 }
2081#endif
2082
Brian Salomon05441c42017-05-15 16:45:49 -04002083 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002084 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002085 SkScalar fXRadius;
2086 SkScalar fYRadius;
2087 SkScalar fInnerXRadius;
2088 SkScalar fInnerYRadius;
2089 SkRect fDevBounds;
2090 };
joshualitt76e7fb62015-02-11 08:52:27 -08002091
Brian Salomon289e3d82016-12-14 15:52:56 -05002092 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002093 Helper fHelper;
2094 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002095 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002096 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002097 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002098
Chris Daltoneb694b72020-03-16 09:25:50 -06002099 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002100 GrProgramInfo* fProgramInfo = nullptr;
2101
John Stiles7571f9e2020-09-02 22:42:33 -04002102 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002103};
2104
joshualitt76e7fb62015-02-11 08:52:27 -08002105/////////////////////////////////////////////////////////////////////////////////////////////////
2106
Brian Salomon05441c42017-05-15 16:45:49 -04002107class DIEllipseOp : public GrMeshDrawOp {
2108private:
2109 using Helper = GrSimpleMeshDrawOpHelper;
2110
2111 struct DeviceSpaceParams {
2112 SkPoint fCenter;
2113 SkScalar fXRadius;
2114 SkScalar fYRadius;
2115 SkScalar fInnerXRadius;
2116 SkScalar fInnerYRadius;
2117 DIEllipseStyle fStyle;
2118 };
2119
joshualitt76e7fb62015-02-11 08:52:27 -08002120public:
Brian Salomon25a88092016-12-01 09:36:50 -05002121 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002122
Herb Derbyc76d4092020-10-07 16:46:15 -04002123 static GrOp::Owner Make(GrRecordingContext* context,
2124 GrPaint&& paint,
2125 const SkMatrix& viewMatrix,
2126 const SkRect& ellipse,
2127 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002128 DeviceSpaceParams params;
2129 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2130 params.fXRadius = SkScalarHalf(ellipse.width());
2131 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002132
bsalomon4b4a7cc2016-07-08 04:42:54 -07002133 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002134 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2135 ? DIEllipseStyle::kStroke
2136 : (SkStrokeRec::kHairline_Style == style)
2137 ? DIEllipseStyle::kHairline
2138 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002139
Brian Salomon05441c42017-05-15 16:45:49 -04002140 params.fInnerXRadius = 0;
2141 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002142 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2143 SkScalar strokeWidth = stroke.getWidth();
2144
2145 if (SkScalarNearlyZero(strokeWidth)) {
2146 strokeWidth = SK_ScalarHalf;
2147 } else {
2148 strokeWidth *= SK_ScalarHalf;
2149 }
2150
2151 // we only handle thick strokes for near-circular ellipses
2152 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002153 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2154 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002155 return nullptr;
2156 }
2157
2158 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002159 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2160 (strokeWidth * strokeWidth) * params.fXRadius) {
2161 return nullptr;
2162 }
2163 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2164 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002165 return nullptr;
2166 }
2167
2168 // set inner radius (if needed)
2169 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002170 params.fInnerXRadius = params.fXRadius - strokeWidth;
2171 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002172 }
2173
Brian Salomon05441c42017-05-15 16:45:49 -04002174 params.fXRadius += strokeWidth;
2175 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002176 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002177
2178 // For large ovals with low precision floats, we fall back to the path renderer.
2179 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2180 // minimum value to avoid divides by zero. With large ovals and low precision this
2181 // leads to blurring at the edge of the oval.
2182 const SkScalar kMaxOvalRadius = 16384;
2183 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2184 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2185 return nullptr;
2186 }
2187
Brian Salomon05441c42017-05-15 16:45:49 -04002188 if (DIEllipseStyle::kStroke == params.fStyle &&
2189 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2190 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002191 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002192 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002193 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002194
Herb Derbyc76d4092020-10-07 16:46:15 -04002195 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002196 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002197 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002198 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002199 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002200 // This expands the outer rect so that after CTM we end up with a half-pixel border
2201 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2202 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2203 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2204 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2205 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2206 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002207
Brian Salomon05441c42017-05-15 16:45:49 -04002208 fEllipses.emplace_back(
2209 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2210 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2211 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2212 params.fCenter.fY - params.fYRadius - geoDy,
2213 params.fCenter.fX + params.fXRadius + geoDx,
2214 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002215 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002216 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002217 }
2218
Brian Salomon289e3d82016-12-14 15:52:56 -05002219 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002220
Chris Dalton1706cbf2019-05-21 19:35:29 -06002221 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002222 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002223 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002224 } else {
2225 fHelper.visitProxies(func);
2226 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002227 }
2228
Chris Dalton6ce447a2019-06-23 18:07:38 -06002229 GrProcessorSet::Analysis finalize(
2230 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
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 Dalton6ce447a2019-06-23 18:07:38 -06002235 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, 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,
Greg Danield358cbe2020-09-11 09:33:54 -04002249 const GrXferProcessor::DstProxyView& 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 Dalton6ce447a2019-06-23 18:07:38 -06002580 GrProcessorSet::Analysis finalize(
2581 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2582 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002583 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002584 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002585 GrProcessorAnalysisCoverage::kSingleChannel, color,
2586 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002587 }
2588
2589 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2590
Brian Salomon92aee3d2016-12-21 09:20:25 -05002591private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002592 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002593 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002594 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002595 SkASSERT(smInset < bigInset);
2596
2597 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002598 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2599 color,
2600 xOffset, 0.0f,
2601 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002602
2603 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002604 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2605 color,
2606 xOffset, 0.0f,
2607 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002608
Brian Osmana1d4eb92018-12-06 16:33:10 -05002609 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2610 color,
2611 0.0f, 0.0f,
2612 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002613
Brian Osmana1d4eb92018-12-06 16:33:10 -05002614 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2615 color,
2616 0.0f, 0.0f,
2617 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002618
Brian Osmana1d4eb92018-12-06 16:33:10 -05002619 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2620 color,
2621 0.0f, 0.0f,
2622 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002623
Brian Osmana1d4eb92018-12-06 16:33:10 -05002624 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2625 color,
2626 0.0f, 0.0f,
2627 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002628
2629 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002630 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2631 color,
2632 xOffset, 0.0f,
2633 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002634
2635 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002636 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2637 color,
2638 xOffset, 0.0f,
2639 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002640 }
2641
Robert Phillips2669a7b2020-03-12 12:07:19 -04002642 GrProgramInfo* programInfo() override { return fProgramInfo; }
2643
Robert Phillips4133dc42020-03-11 15:55:55 -04002644 void onCreateProgramInfo(const GrCaps* caps,
2645 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002646 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002647 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002648 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002649 GrXferBarrierFlags renderPassXferBarriers,
2650 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002651 // Invert the view matrix as a local matrix (if any other processors require coords).
2652 SkMatrix localMatrix;
2653 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002654 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002655 }
2656
Robert Phillips4490d922020-03-03 14:50:59 -05002657 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002658 false, false, false, false,
2659 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002660
Brian Salomon8afde5f2020-04-01 16:22:00 -04002661 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002662 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002663 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002664 }
2665
Robert Phillips4490d922020-03-03 14:50:59 -05002666 void onPrepareDraws(Target* target) override {
2667 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002668 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002669 if (!fProgramInfo) {
2670 return;
2671 }
2672 }
2673
Brian Salomon12d22642019-01-29 14:38:50 -05002674 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002675 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002676
Robert Phillips787fd9d2021-03-22 14:48:09 -04002677 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002678 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002679 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002680 SkDebugf("Could not allocate vertices\n");
2681 return;
2682 }
2683
Brian Salomon12d22642019-01-29 14:38:50 -05002684 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002685 int firstIndex = 0;
2686 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2687 if (!indices) {
2688 SkDebugf("Could not allocate indices\n");
2689 return;
2690 }
2691
2692 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002693 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002694 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002695 SkScalar outerRadius = rrect.fOuterRadius;
2696 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002697
Brian Salomon289e3d82016-12-14 15:52:56 -05002698 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2699 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002700
Brian Salomon289e3d82016-12-14 15:52:56 -05002701 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002702 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002703 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002704 SkScalar innerRadius = rrect.fType != kFill_RRectType
2705 ? rrect.fInnerRadius / rrect.fOuterRadius
2706 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002707 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002708 verts.write(bounds.fLeft, yCoords[i],
2709 color,
2710 -1.0f, yOuterRadii[i],
2711 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002712
Brian Osmana1d4eb92018-12-06 16:33:10 -05002713 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2714 color,
2715 0.0f, yOuterRadii[i],
2716 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002717
Brian Osmana1d4eb92018-12-06 16:33:10 -05002718 verts.write(bounds.fRight - outerRadius, yCoords[i],
2719 color,
2720 0.0f, yOuterRadii[i],
2721 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002722
Brian Osmana1d4eb92018-12-06 16:33:10 -05002723 verts.write(bounds.fRight, yCoords[i],
2724 color,
2725 1.0f, yOuterRadii[i],
2726 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002727 }
jvanverthc3d0e422016-08-25 08:12:35 -07002728 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002729 // Effectively this is an additional stroked rrect, with its
2730 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2731 // This will give us correct AA in the center and the correct
2732 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002733 //
jvanvertha4f1af82016-08-29 07:17:47 -07002734 // Also, the outer offset is a constant vector pointing to the right, which
2735 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002736 if (kOverstroke_RRectType == rrect.fType) {
2737 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002738
Brian Salomon05441c42017-05-15 16:45:49 -04002739 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002740 // this is the normalized distance from the outer rectangle of this
2741 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002742 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002743
Brian Osmana1d4eb92018-12-06 16:33:10 -05002744 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002745 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002746 }
jvanverth6a397612016-08-26 08:15:33 -07002747
Brian Salomon05441c42017-05-15 16:45:49 -04002748 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2749 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002750 for (int i = 0; i < primIndexCount; ++i) {
2751 *indices++ = primIndices[i] + currStartVertex;
2752 }
2753
Brian Salomon05441c42017-05-15 16:45:49 -04002754 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002755 }
2756
Robert Phillips4490d922020-03-03 14:50:59 -05002757 fMesh = target->allocMesh();
2758 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002759 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002760 }
2761
2762 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002763 if (!fProgramInfo || !fMesh) {
2764 return;
2765 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002766
Chris Dalton765ed362020-03-16 17:34:44 -06002767 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002768 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002769 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002770 }
2771
Herb Derbye25c3002020-10-27 15:57:27 -04002772 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002773 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002774
2775 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002776 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002777 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002778 }
2779
Brian Salomon05441c42017-05-15 16:45:49 -04002780 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002781 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002782 }
2783
Brian Salomon05441c42017-05-15 16:45:49 -04002784 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002785 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2786 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002787 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002788 }
2789
Brian Salomon05441c42017-05-15 16:45:49 -04002790 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002791 fVertCount += that->fVertCount;
2792 fIndexCount += that->fIndexCount;
2793 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002794 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002795 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002796 }
2797
John Stilesaf366522020-08-13 09:57:34 -04002798#if GR_TEST_UTILS
2799 SkString onDumpInfo() const override {
2800 SkString string;
2801 for (int i = 0; i < fRRects.count(); ++i) {
2802 string.appendf(
2803 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2804 "InnerRad: %.2f, OuterRad: %.2f\n",
2805 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2806 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2807 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2808 fRRects[i].fOuterRadius);
2809 }
2810 string += fHelper.dumpInfo();
2811 return string;
2812 }
2813#endif
2814
Brian Salomon05441c42017-05-15 16:45:49 -04002815 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002816 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002817 SkScalar fInnerRadius;
2818 SkScalar fOuterRadius;
2819 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002820 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002821 };
2822
Brian Salomon289e3d82016-12-14 15:52:56 -05002823 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002824 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002825 int fVertCount;
2826 int fIndexCount;
2827 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002828 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002829 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002830
Chris Daltoneb694b72020-03-16 09:25:50 -06002831 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002832 GrProgramInfo* fProgramInfo = nullptr;
2833
John Stiles7571f9e2020-09-02 22:42:33 -04002834 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002835};
2836
jvanverth84839f62016-08-29 10:16:40 -07002837static const int kNumRRectsInIndexBuffer = 256;
2838
2839GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2840GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002841static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2842 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002843 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2844 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2845 switch (type) {
2846 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002847 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002848 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2849 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002850 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002851 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002852 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2853 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002854 default:
2855 SkASSERT(false);
2856 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002857 }
jvanverth84839f62016-08-29 10:16:40 -07002858}
2859
Brian Salomon05441c42017-05-15 16:45:49 -04002860class EllipticalRRectOp : public GrMeshDrawOp {
2861private:
2862 using Helper = GrSimpleMeshDrawOpHelper;
2863
joshualitt76e7fb62015-02-11 08:52:27 -08002864public:
Brian Salomon25a88092016-12-01 09:36:50 -05002865 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002866
bsalomon4b4a7cc2016-07-08 04:42:54 -07002867 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2868 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002869 static GrOp::Owner Make(GrRecordingContext* context,
2870 GrPaint&& paint,
2871 const SkMatrix& viewMatrix,
2872 const SkRect& devRect,
2873 float devXRadius,
2874 float devYRadius,
2875 SkVector devStrokeWidths,
2876 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002877 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2878 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002879 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2880 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002881 if (devStrokeWidths.fX > 0) {
2882 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2883 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2884 } else {
2885 devStrokeWidths.scale(SK_ScalarHalf);
2886 }
joshualitt76e7fb62015-02-11 08:52:27 -08002887
bsalomon4b4a7cc2016-07-08 04:42:54 -07002888 // we only handle thick strokes for near-circular ellipses
2889 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002890 (SK_ScalarHalf * devXRadius > devYRadius ||
2891 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002892 return nullptr;
2893 }
2894
2895 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002896 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2897 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002898 return nullptr;
2899 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002900 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2901 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002902 return nullptr;
2903 }
Brian Salomon05441c42017-05-15 16:45:49 -04002904 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002905 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002906 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002907 devXRadius, devYRadius, devStrokeWidths,
2908 strokeOnly);
2909 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002910
Herb Derbyc76d4092020-10-07 16:46:15 -04002911 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002912 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2913 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002914 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002915 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002916 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002917 SkScalar innerXRadius = 0.0f;
2918 SkScalar innerYRadius = 0.0f;
2919 SkRect bounds = devRect;
2920 bool stroked = false;
2921 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002922 // this is legit only if scale & translation (which should be the case at the moment)
2923 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002924 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2925 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002926 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2927 }
2928
Brian Salomon05441c42017-05-15 16:45:49 -04002929 devXRadius += devStrokeHalfWidths.fX;
2930 devYRadius += devStrokeHalfWidths.fY;
2931 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002932 }
2933
Brian Salomon05441c42017-05-15 16:45:49 -04002934 fStroked = stroked;
2935 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002936 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002937 // Expand the rect for aa in order to generate the correct vertices.
2938 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002939 fRRects.emplace_back(
2940 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002941 }
2942
Brian Salomon289e3d82016-12-14 15:52:56 -05002943 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002944
Chris Dalton1706cbf2019-05-21 19:35:29 -06002945 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002946 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002947 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002948 } else {
2949 fHelper.visitProxies(func);
2950 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002951 }
2952
Chris Dalton6ce447a2019-06-23 18:07:38 -06002953 GrProcessorSet::Analysis finalize(
2954 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2955 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002956 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002957 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002958 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002959 GrProcessorAnalysisCoverage::kSingleChannel, color,
2960 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002961 }
2962
2963 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2964
bsalomone46f9fe2015-08-18 06:05:14 -07002965private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002966 GrProgramInfo* programInfo() override { return fProgramInfo; }
2967
Robert Phillips4133dc42020-03-11 15:55:55 -04002968 void onCreateProgramInfo(const GrCaps* caps,
2969 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002970 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002972 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002973 GrXferBarrierFlags renderPassXferBarriers,
2974 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002975 SkMatrix localMatrix;
2976 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002977 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002978 }
2979
Robert Phillips4490d922020-03-03 14:50:59 -05002980 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2981 fUseScale, localMatrix);
2982
Brian Salomon8afde5f2020-04-01 16:22:00 -04002983 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002984 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002985 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002986 }
2987
Robert Phillips4490d922020-03-03 14:50:59 -05002988 void onPrepareDraws(Target* target) override {
2989 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002990 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002991 if (!fProgramInfo) {
2992 return;
2993 }
2994 }
joshualitt76e7fb62015-02-11 08:52:27 -08002995
bsalomonb5238a72015-05-05 07:49:49 -07002996 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002997 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002998 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2999 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08003000
Brian Salomon12d22642019-01-29 14:38:50 -05003001 if (!indexBuffer) {
3002 SkDebugf("Could not allocate indices\n");
3003 return;
3004 }
Robert Phillips4490d922020-03-03 14:50:59 -05003005 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04003006 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003007 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003008 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003009 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003010 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003011 SkDebugf("Could not allocate vertices\n");
3012 return;
3013 }
3014
Brian Salomon05441c42017-05-15 16:45:49 -04003015 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003016 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003017 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003018 float reciprocalRadii[4] = {
3019 SkScalarInvert(rrect.fXRadius),
3020 SkScalarInvert(rrect.fYRadius),
3021 SkScalarInvert(rrect.fInnerXRadius),
3022 SkScalarInvert(rrect.fInnerYRadius)
3023 };
joshualitt76e7fb62015-02-11 08:52:27 -08003024
Brian Osmane3afdd52020-10-28 10:49:56 -04003025 // If the stroke width is exactly double the radius, the inner radii will be zero.
3026 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
3027 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
3028 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3029
joshualitt76e7fb62015-02-11 08:52:27 -08003030 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003031 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3032 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003033
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003034 SkScalar xMaxOffset = xOuterRadius;
3035 SkScalar yMaxOffset = yOuterRadius;
3036 if (!fStroked) {
3037 // For filled rrects we map a unit circle in the vertex attributes rather than
3038 // computing an ellipse and modifying that distance, so we normalize to 1.
3039 xMaxOffset /= rrect.fXRadius;
3040 yMaxOffset /= rrect.fYRadius;
3041 }
3042
Brian Salomon05441c42017-05-15 16:45:49 -04003043 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003044
Brian Salomon289e3d82016-12-14 15:52:56 -05003045 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3046 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003047 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003048 SK_ScalarNearlyZero, // we're using inversesqrt() in
3049 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003050 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003051
Brian Osman788b9162020-02-07 10:36:46 -05003052 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003053 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003054 verts.write(bounds.fLeft, yCoords[i],
3055 color,
3056 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003057 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003059
Brian Osmana1d4eb92018-12-06 16:33:10 -05003060 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3061 color,
3062 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003063 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003064 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003065
Brian Osmana1d4eb92018-12-06 16:33:10 -05003066 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3067 color,
3068 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003069 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003070 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003071
Brian Osmana1d4eb92018-12-06 16:33:10 -05003072 verts.write(bounds.fRight, yCoords[i],
3073 color,
3074 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003075 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003076 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003077 }
3078 }
Robert Phillips4490d922020-03-03 14:50:59 -05003079 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003080 }
3081
3082 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003083 if (!fProgramInfo || !fMesh) {
3084 return;
3085 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003086
Chris Dalton765ed362020-03-16 17:34:44 -06003087 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003088 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003089 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003090 }
3091
Herb Derbye25c3002020-10-27 15:57:27 -04003092 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003093 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003094
Brian Salomon05441c42017-05-15 16:45:49 -04003095 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003096 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003097 }
3098
bsalomoncdaa97b2016-03-08 08:30:14 -08003099 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003100 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003101 }
3102
Brian Salomon05441c42017-05-15 16:45:49 -04003103 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003104 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3105 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003106 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003107 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003108
Brian Salomon05441c42017-05-15 16:45:49 -04003109 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003110 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003111 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003112 }
3113
John Stilesaf366522020-08-13 09:57:34 -04003114#if GR_TEST_UTILS
3115 SkString onDumpInfo() const override {
3116 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3117 for (const auto& geo : fRRects) {
3118 string.appendf(
3119 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3120 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3121 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3122 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3123 geo.fInnerXRadius, geo.fInnerYRadius);
3124 }
3125 string += fHelper.dumpInfo();
3126 return string;
3127 }
3128#endif
3129
Brian Salomon05441c42017-05-15 16:45:49 -04003130 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003131 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003132 SkScalar fXRadius;
3133 SkScalar fYRadius;
3134 SkScalar fInnerXRadius;
3135 SkScalar fInnerYRadius;
3136 SkRect fDevBounds;
3137 };
3138
Brian Salomon289e3d82016-12-14 15:52:56 -05003139 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003140 Helper fHelper;
3141 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003142 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003143 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003144 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003145
Chris Daltoneb694b72020-03-16 09:25:50 -06003146 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003147 GrProgramInfo* fProgramInfo = nullptr;
3148
John Stiles7571f9e2020-09-02 22:42:33 -04003149 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003150};
3151
Herb Derbyc76d4092020-10-07 16:46:15 -04003152GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3153 GrPaint&& paint,
3154 const SkMatrix& viewMatrix,
3155 const SkRRect& rrect,
3156 const SkStrokeRec& stroke,
3157 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003158 SkASSERT(viewMatrix.rectStaysRect());
3159 SkASSERT(viewMatrix.isSimilarity());
3160 SkASSERT(rrect.isSimple());
3161 SkASSERT(!rrect.isOval());
3162 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3163
3164 // RRect ops only handle simple, but not too simple, rrects.
3165 // Do any matrix crunching before we reset the draw state for device coords.
3166 const SkRect& rrectBounds = rrect.getBounds();
3167 SkRect bounds;
3168 viewMatrix.mapRect(&bounds, rrectBounds);
3169
3170 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3171 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3172 viewMatrix[SkMatrix::kMSkewY]));
3173
3174 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3175 SkScalar scaledStroke = -1;
3176 SkScalar strokeWidth = stroke.getWidth();
3177 SkStrokeRec::Style style = stroke.getStyle();
3178
3179 bool isStrokeOnly =
3180 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3181 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3182
3183 if (hasStroke) {
3184 if (SkStrokeRec::kHairline_Style == style) {
3185 scaledStroke = SK_Scalar1;
3186 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003187 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3188 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003189 }
3190 }
3191
3192 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3193 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3194 // patch will have fractional coverage. This only matters when the interior is actually filled.
3195 // We could consider falling back to rect rendering here, since a tiny radius is
3196 // indistinguishable from a square corner.
3197 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3198 return nullptr;
3199 }
3200
3201 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3202 scaledStroke, isStrokeOnly);
3203}
3204
Herb Derbyc76d4092020-10-07 16:46:15 -04003205GrOp::Owner make_rrect_op(GrRecordingContext* context,
3206 GrPaint&& paint,
3207 const SkMatrix& viewMatrix,
3208 const SkRRect& rrect,
3209 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003210 SkASSERT(viewMatrix.rectStaysRect());
3211 SkASSERT(rrect.isSimple());
3212 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003213
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003214 // RRect ops only handle simple, but not too simple, rrects.
3215 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003216 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003217 SkRect bounds;
3218 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003219
Mike Reed242135a2018-02-22 13:41:39 -05003220 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003221 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3222 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3223 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3224 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003225
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003226 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003227
bsalomon4b4a7cc2016-07-08 04:42:54 -07003228 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3229 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003230 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003231
Brian Salomon289e3d82016-12-14 15:52:56 -05003232 bool isStrokeOnly =
3233 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003234 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3235
3236 if (hasStroke) {
3237 if (SkStrokeRec::kHairline_Style == style) {
3238 scaledStroke.set(1, 1);
3239 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003240 scaledStroke.fX = SkScalarAbs(
3241 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3242 scaledStroke.fY = SkScalarAbs(
3243 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003244 }
3245
Jim Van Verth64b85892019-06-17 12:01:46 -04003246 // if half of strokewidth is greater than radius, we don't handle that right now
3247 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3248 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003249 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003250 }
3251 }
3252
Brian Salomon8a97f562019-04-18 14:07:27 -04003253 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003254 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003255 std::swap(xRadius, yRadius);
3256 std::swap(scaledStroke.fX, scaledStroke.fY);
3257 }
3258
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003259 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3260 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3261 // patch will have fractional coverage. This only matters when the interior is actually filled.
3262 // We could consider falling back to rect rendering here, since a tiny radius is
3263 // indistinguishable from a square corner.
3264 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003265 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003266 }
3267
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003268 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003269 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3270 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003271}
3272
Herb Derbyc76d4092020-10-07 16:46:15 -04003273GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3274 GrPaint&& paint,
3275 const SkMatrix& viewMatrix,
3276 const SkRRect& rrect,
3277 const SkStrokeRec& stroke,
3278 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003279 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003280 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003281 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003282 }
3283
3284 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003285 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003286 }
3287
Greg Daniel2655ede2019-04-10 00:49:28 +00003288 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003289}
joshualitt3e708c52015-04-30 13:49:27 -07003290
bsalomon4b4a7cc2016-07-08 04:42:54 -07003291///////////////////////////////////////////////////////////////////////////////
3292
Herb Derbyc76d4092020-10-07 16:46:15 -04003293GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3294 GrPaint&& paint,
3295 const SkMatrix& viewMatrix,
3296 const SkRect& oval,
3297 const GrStyle& style,
3298 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003299 SkScalar width = oval.width();
3300 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3301 circle_stays_circle(viewMatrix));
3302
3303 auto r = width / 2.f;
3304 SkPoint center = { oval.centerX(), oval.centerY() };
3305 if (style.hasNonDashPathEffect()) {
3306 return nullptr;
3307 } else if (style.isDashed()) {
3308 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3309 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3310 return nullptr;
3311 }
3312 auto onInterval = style.dashIntervals()[0];
3313 auto offInterval = style.dashIntervals()[1];
3314 if (offInterval == 0) {
3315 GrStyle strokeStyle(style.strokeRec(), nullptr);
3316 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3317 strokeStyle, shaderCaps);
3318 } else if (onInterval == 0) {
3319 // There is nothing to draw but we have no way to indicate that here.
3320 return nullptr;
3321 }
3322 auto angularOnInterval = onInterval / r;
3323 auto angularOffInterval = offInterval / r;
3324 auto phaseAngle = style.dashPhase() / r;
3325 // Currently this function doesn't accept ovals with different start angles, though
3326 // it could.
3327 static const SkScalar kStartAngle = 0.f;
3328 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3329 style.strokeRec().getWidth(), kStartAngle,
3330 angularOnInterval, angularOffInterval, phaseAngle);
3331 }
3332 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3333}
3334
Herb Derbyc76d4092020-10-07 16:46:15 -04003335GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3336 GrPaint&& paint,
3337 const SkMatrix& viewMatrix,
3338 const SkRect& oval,
3339 const GrStyle& style,
3340 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003341 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003342 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003343 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3344 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003345 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003346 }
3347
3348 if (style.pathEffect()) {
3349 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003350 }
3351
Stan Ilieveb868aa2017-02-21 11:06:16 -05003352 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003353 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003354 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003355 }
3356
Stan Ilieveb868aa2017-02-21 11:06:16 -05003357 // Otherwise, if we have shader derivative support, render as device-independent
3358 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003359 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3360 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3361 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3362 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3363 // Check for near-degenerate matrix
3364 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003365 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003366 style.strokeRec());
3367 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003368 }
3369
bsalomon4b4a7cc2016-07-08 04:42:54 -07003370 return nullptr;
3371}
3372
3373///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003374
Herb Derbyc76d4092020-10-07 16:46:15 -04003375GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3376 GrPaint&& paint,
3377 const SkMatrix& viewMatrix,
3378 const SkRect& oval, SkScalar startAngle,
3379 SkScalar sweepAngle, bool useCenter,
3380 const GrStyle& style,
3381 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003382 SkASSERT(!oval.isEmpty());
3383 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003384 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003385 if (SkScalarAbs(sweepAngle) >= 360.f) {
3386 return nullptr;
3387 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003388 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3389 return nullptr;
3390 }
3391 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003392 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3393 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003394 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003395 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003396}
3397
3398///////////////////////////////////////////////////////////////////////////////
3399
Hal Canary6f6961e2017-01-31 13:50:44 -05003400#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003401
Brian Salomon05441c42017-05-15 16:45:49 -04003402GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003403 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003404 SkScalar rotate = random->nextSScalar1() * 360.f;
3405 SkScalar translateX = random->nextSScalar1() * 1000.f;
3406 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003407 SkScalar scale;
3408 do {
3409 scale = random->nextSScalar1() * 100.f;
3410 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003411 SkMatrix viewMatrix;
3412 viewMatrix.setRotate(rotate);
3413 viewMatrix.postTranslate(translateX, translateY);
3414 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003415 SkRect circle = GrTest::TestSquare(random);
3416 SkPoint center = {circle.centerX(), circle.centerY()};
3417 SkScalar radius = circle.width() / 2.f;
3418 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003419 CircleOp::ArcParams arcParamsTmp;
3420 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003421 if (random->nextBool()) {
3422 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003423 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3424 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003425 arcParams = &arcParamsTmp;
3426 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003427 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3428 center, radius,
3429 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003430 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003431 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003432 }
Mike Klein16885072018-12-11 09:54:31 -05003433 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003434 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003435}
3436
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003437GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3438 SkScalar rotate = random->nextSScalar1() * 360.f;
3439 SkScalar translateX = random->nextSScalar1() * 1000.f;
3440 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003441 SkScalar scale;
3442 do {
3443 scale = random->nextSScalar1() * 100.f;
3444 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003445 SkMatrix viewMatrix;
3446 viewMatrix.setRotate(rotate);
3447 viewMatrix.postTranslate(translateX, translateY);
3448 viewMatrix.postScale(scale, scale);
3449 SkRect circle = GrTest::TestSquare(random);
3450 SkPoint center = {circle.centerX(), circle.centerY()};
3451 SkScalar radius = circle.width() / 2.f;
3452 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3453 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3454 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3455 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3456 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003457 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3458 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003459 startAngle, onAngle, offAngle, phase);
3460}
3461
Brian Salomon05441c42017-05-15 16:45:49 -04003462GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003463 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003464 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003465 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003466 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003467}
3468
Brian Salomon05441c42017-05-15 16:45:49 -04003469GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003470 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003471 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003472 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003473 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003474}
3475
Jim Van Verth64b85892019-06-17 12:01:46 -04003476GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3477 do {
3478 SkScalar rotate = random->nextSScalar1() * 360.f;
3479 SkScalar translateX = random->nextSScalar1() * 1000.f;
3480 SkScalar translateY = random->nextSScalar1() * 1000.f;
3481 SkScalar scale;
3482 do {
3483 scale = random->nextSScalar1() * 100.f;
3484 } while (scale == 0);
3485 SkMatrix viewMatrix;
3486 viewMatrix.setRotate(rotate);
3487 viewMatrix.postTranslate(translateX, translateY);
3488 viewMatrix.postScale(scale, scale);
3489 SkRect rect = GrTest::TestRect(random);
3490 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3491 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3492 if (rrect.isOval()) {
3493 continue;
3494 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003495 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003496 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3497 GrTest::TestStrokeRec(random), nullptr);
3498 if (op) {
3499 return op;
3500 }
3501 assert_alive(paint);
3502 } while (true);
3503}
3504
Brian Salomon05441c42017-05-15 16:45:49 -04003505GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003506 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003507 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003508 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003509 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003510}
3511
3512#endif