blob: f720f638607f9118176be31117b0c4be641dad04 [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"
9#include "src/core/SkRRectPriv.h"
10#include "src/gpu/GrCaps.h"
11#include "src/gpu/GrDrawOpTest.h"
12#include "src/gpu/GrGeometryProcessor.h"
13#include "src/gpu/GrOpFlushState.h"
14#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050015#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/GrResourceProvider.h"
17#include "src/gpu/GrShaderCaps.h"
18#include "src/gpu/GrStyle.h"
19#include "src/gpu/GrVertexWriter.h"
20#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
21#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
22#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
23#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/glsl/GrGLSLVarying.h"
25#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
26#include "src/gpu/ops/GrMeshDrawOp.h"
27#include "src/gpu/ops/GrOvalOpFactory.h"
28#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080029
Ben Wagnerf08d1d02018-06-18 15:11:00 -040030#include <utility>
31
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080033
Brian Salomon289e3d82016-12-14 15:52:56 -050034static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050035
Brian Osman2b6e3902018-11-21 15:29:43 -050036// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
37static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
38 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
39};
40
commit-bot@chromium.org81312832013-03-22 18:34:09 +000041}
42
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000043///////////////////////////////////////////////////////////////////////////////
44
45/**
bsalomonce1c8862014-12-15 07:11:22 -080046 * The output of this effect is a modulation of the input color and coverage for a circle. It
47 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080048 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080049 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080050 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080051 * vec4f : (p.xy, outerRad, innerRad)
52 * p is the position in the normalized space.
53 * outerRad is the outerRadius in device space.
54 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070055 * Additional clip planes are supported for rendering circular arcs. The additional planes are
56 * either intersected or unioned together. Up to three planes are supported (an initial plane,
57 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050058 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070059 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040060 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
61 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000062 */
63
bsalomoncdaa97b2016-03-08 08:30:14 -080064class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000065public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050066 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
67 bool isectPlane, bool unionPlane, bool roundCaps,
68 bool wideColor, const SkMatrix& localMatrix) {
69 return arena->make<CircleGeometryProcessor>(stroke, clipPlane, isectPlane, unionPlane,
70 roundCaps, wideColor, localMatrix);
71 }
72
73 const char* name() const override { return "CircleGeometryProcessor"; }
74
75 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
76 GLSLProcessor::GenKey(*this, caps, b);
77 }
78
79 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
80 return new GLSLProcessor();
81 }
82
83private:
84 friend class ::SkArenaAlloc; // for access to ctor
85
Greg Daniel2655ede2019-04-10 00:49:28 +000086 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050087 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040088 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040089 , fLocalMatrix(localMatrix)
90 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050091 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050092 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050093 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
94
bsalomon4f3a0ca2016-08-22 13:14:26 -070095 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040096 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070097 }
98 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040099 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700100 }
101 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400102 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700103 }
Brian Salomon45c92202018-04-10 10:53:58 -0400104 if (roundCaps) {
105 SkASSERT(stroke);
106 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400107 fInRoundCapCenters =
108 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400109 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500110 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111 }
112
egdaniel57d3b032015-11-13 11:57:27 -0800113 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000114 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800115 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000116
Brian Salomon289e3d82016-12-14 15:52:56 -0500117 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800118 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800119 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800120 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800121 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700122 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800123
joshualittabb52a12015-01-13 15:02:10 -0800124 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800125 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400126 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500127 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400128 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400129 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
131 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400132 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400133 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
135 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400136 if (cgp.fInUnionPlane.isInitialized()) {
137 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400138 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700139 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
140 }
Brian Salomon45c92202018-04-10 10:53:58 -0400141 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400142 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400143 fragBuilder->codeAppend("float4 roundCapCenters;");
144 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
145 varyingHandler->addVarying("capRadius", &capRadius,
146 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
147 // This is the cap radius in normalized space where the outer radius is 1 and
148 // circledEdge.w is the normalized inner radius.
149 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500150 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400151 }
joshualittabb52a12015-01-13 15:02:10 -0800152
joshualittb8c241a2015-05-19 08:23:30 -0700153 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500154 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800155
joshualittabb52a12015-01-13 15:02:10 -0800156 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500157 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400158 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
159 cgp.fInPosition.asShaderVar(), cgp.fLocalMatrix,
160 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800161
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400162 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500163 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000164 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800165 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500166 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500167 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000168 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800169 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000170 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000171
Brian Salomon92be2f72018-06-19 14:33:47 -0400172 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500173 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000174 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
175 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400176 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500177 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000178 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
179 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700180 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400181 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500182 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000183 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
184 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700185 }
186 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400187 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400188 // We compute coverage of the round caps as circles at the butt caps produced
189 // by the clip planes. The inverse of the clip planes is applied so that there
190 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400191 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500192 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
193 " roundCapCenters.xy)));"
194 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
195 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400196 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400197 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
198 capRadius.fsIn(), capRadius.fsIn());
199 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700200 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000201 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202 }
203
robertphillips46d36f02015-01-18 08:14:14 -0800204 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500205 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700206 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800207 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400208 uint32_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500209 key = cgp.fStroke ? 0x01 : 0x0;
Michael Ludwig553db622020-06-19 10:47:30 -0400210 key |= cgp.fInClipPlane.isInitialized() ? 0x02 : 0x0;
211 key |= cgp.fInIsectPlane.isInitialized() ? 0x04 : 0x0;
212 key |= cgp.fInUnionPlane.isInitialized() ? 0x08 : 0x0;
213 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x10 : 0x0;
214 key |= (ComputeMatrixKey(cgp.fLocalMatrix) << 16);
joshualittb8c241a2015-05-19 08:23:30 -0700215 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 }
217
bsalomona624bf32016-09-20 09:12:47 -0700218 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500219 const CoordTransformRange& transformRange) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400220 this->setTransformDataHelper(pdman, transformRange);
221 this->setTransform(pdman, fLocalMatrixUniform,
222 primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
223 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700224 }
225
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 private:
egdaniele659a582015-11-13 09:55:43 -0800227 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400228
229 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
230 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 };
232
Brian Salomon289e3d82016-12-14 15:52:56 -0500233 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400234
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500235 Attribute fInPosition;
236 Attribute fInColor;
237 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400238 // Optional attributes.
239 Attribute fInClipPlane;
240 Attribute fInIsectPlane;
241 Attribute fInUnionPlane;
242 Attribute fInRoundCapCenters;
243
Brian Salomon289e3d82016-12-14 15:52:56 -0500244 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400245 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246
joshualitt249af152014-09-15 11:41:13 -0700247 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248};
249
bsalomoncdaa97b2016-03-08 08:30:14 -0800250GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
Hal Canary6f6961e2017-01-31 13:50:44 -0500252#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500253GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400254 bool stroke = d->fRandom->nextBool();
255 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500256 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400257 bool clipPlane = d->fRandom->nextBool();
258 bool isectPlane = d->fRandom->nextBool();
259 bool unionPlane = d->fRandom->nextBool();
260 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
262 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263}
Hal Canary6f6961e2017-01-31 13:50:44 -0500264#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400266class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
267public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500268 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
269 const SkMatrix& localMatrix) {
270 return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400271 }
272
273 ~ButtCapDashedCircleGeometryProcessor() override {}
274
275 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
276
277 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
278 GLSLProcessor::GenKey(*this, caps, b);
279 }
280
281 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
282 return new GLSLProcessor();
283 }
284
285private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500286 friend class ::SkArenaAlloc; // for access to ctor
287
288 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
289 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
290 , fLocalMatrix(localMatrix) {
291 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
292 fInColor = MakeColorAttribute("inColor", wideColor);
293 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
295 this->setVertexAttributes(&fInPosition, 4);
296 }
297
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298 class GLSLProcessor : public GrGLSLGeometryProcessor {
299 public:
300 GLSLProcessor() {}
301
302 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
303 const ButtCapDashedCircleGeometryProcessor& bcscgp =
304 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
305 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
306 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
307 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
308 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
309
310 // emit attributes
311 varyingHandler->emitAttributes(bcscgp);
312 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500313 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314
315 fragBuilder->codeAppend("float4 dashParams;");
316 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500317 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400318 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
319 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
320 varyingHandler->addVarying("wrapDashes", &wrapDashes,
321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
322 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
323 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
324 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
327 // x = length of on interval, y = length of on + off.
328 // There are two other parameters in dashParams.zw:
329 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
330 // Each interval has a "corresponding" dash which may be shifted partially or
331 // fully out of its interval by the phase. So there may be up to two "visual"
332 // dashes in an interval.
333 // When computing coverage in an interval we look at three dashes. These are the
334 // "corresponding" dashes from the current, previous, and next intervals. Any of these
335 // may be phase shifted into our interval or even when phase=0 they may be within half a
336 // pixel distance of a pixel center in the interval.
337 // When in the first interval we need to check the dash from the last interval. And
338 // similarly when in the last interval we need to check the dash from the first
339 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
340 // We compute the dash begin/end angles in the vertex shader and apply them in the
341 // fragment shader when we detect we're in the first/last interval.
342 vertBuilder->codeAppend(R"(
343 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
344 // to the fragment shader as a varying.
345 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500346 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400347 // We can happen to be perfectly divisible.
348 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500349 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400350 }
351 // Let 'l' be the last interval before reaching 2 pi.
352 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
353 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
354 // interval.
355 half offset = 0;
356 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500359 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400360 }
361 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
362 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
363 // min.
364 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
365
366 // Based on the phase determine whether the -1st, 0th, or 1st interval's
367 // "corresponding" dash appears in the 0th interval and is closest to l.
368 offset = 0;
369 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500370 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400371 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500372 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400373 }
374 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
375 wrapDashes.w = wrapDashes.z + dashParams.x;
376 // The start of the dash we're considering may be clipped by the start of the
377 // circle.
378 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
379 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
382 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
383 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
384
385 // setup pass through color
386 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500387 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
389
390 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500391 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400392 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
393 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
394 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
400 SkString fnName;
401 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
402 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400406 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400407 )",
408 &fnName);
409 fragBuilder->codeAppend(R"(
410 float d = length(circleEdge.xy) * circleEdge.z;
411
412 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500413 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400414 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500415 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400416 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400417 edgeAlpha *= innerAlpha;
418
Ethan Nicholase1f55022019-02-05 17:17:40 -0500419 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400420 angleFromStart = mod(angleFromStart, 6.28318530718);
421 float x = mod(angleFromStart, dashParams.y);
422 // Convert the radial distance from center to pixel into a diameter.
423 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500424 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
425 half(dashParams.w));
426 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
427 half(dashParams.y) + half(dashParams.x) -
428 half(dashParams.w));
429 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
430 half(-dashParams.y) + half(dashParams.x) -
431 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 half dashAlpha = 0;
433 )");
434 fragBuilder->codeAppendf(R"(
435 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400437 currDash.y = min(currDash.y, lastIntervalLength);
438 if (nextDash.x >= lastIntervalLength) {
439 // The next dash is outside the 0..2pi range, throw it away
440 nextDash.xy = half2(1000);
441 } else {
442 // Clip the end of the next dash to the end of the circle
443 nextDash.y = min(nextDash.y, lastIntervalLength);
444 }
445 }
446 )", fnName.c_str(), fnName.c_str());
447 fragBuilder->codeAppendf(R"(
448 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500449 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400450 currDash.x = max(currDash.x, 0);
451 if (prevDash.y <= 0) {
452 // The previous dash is outside the 0..2pi range, throw it away
453 prevDash.xy = half2(1000);
454 } else {
455 // Clip the start previous dash to the start of the circle
456 prevDash.x = max(prevDash.x, 0);
457 }
458 }
459 )", fnName.c_str(), fnName.c_str());
460 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
462 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
463 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400464 dashAlpha = min(dashAlpha, 1);
465 edgeAlpha *= dashAlpha;
466 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
467 fnName.c_str());
468 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
469 }
470
471 static void GenKey(const GrGeometryProcessor& gp,
472 const GrShaderCaps&,
473 GrProcessorKeyBuilder* b) {
474 const ButtCapDashedCircleGeometryProcessor& bcscgp =
475 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400476 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400477 }
478
479 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500480 const CoordTransformRange& transformRange) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400481 this->setTransformDataHelper(pdman, transformRange);
482 this->setTransform(pdman, fLocalMatrixUniform,
483 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
484 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 }
486
487 private:
488 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400489
490 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
491 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400492 };
493
494 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500495 Attribute fInPosition;
496 Attribute fInColor;
497 Attribute fInCircleEdge;
498 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400499
500 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
501
502 typedef GrGeometryProcessor INHERITED;
503};
504
505#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500506GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500507 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500509 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400510}
511#endif
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/**
516 * 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 +0000517 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
518 * in both x and y directions.
519 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000520 * 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 +0000521 */
522
bsalomoncdaa97b2016-03-08 08:30:14 -0800523class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500525 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
526 bool useScale, const SkMatrix& localMatrix) {
527 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
528 }
529
530 ~EllipseGeometryProcessor() override {}
531
532 const char* name() const override { return "EllipseGeometryProcessor"; }
533
534 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
535 GLSLProcessor::GenKey(*this, caps, b);
536 }
537
538 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
539 return new GLSLProcessor();
540 }
541
542private:
543 friend class ::SkArenaAlloc; // for access to ctor
544
Greg Daniel2655ede2019-04-10 00:49:28 +0000545 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400546 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500547 : INHERITED(kEllipseGeometryProcessor_ClassID)
548 , fLocalMatrix(localMatrix)
549 , fStroke(stroke)
550 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500551 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500552 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400553 if (useScale) {
554 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
555 } else {
556 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
557 }
558 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500559 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000560 }
561
egdaniel57d3b032015-11-13 11:57:27 -0800562 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800564 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000565
Brian Salomon289e3d82016-12-14 15:52:56 -0500566 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800567 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800568 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800569 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800570 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000571
joshualittabb52a12015-01-13 15:02:10 -0800572 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800573 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800574
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400575 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
576 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800577 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800578 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500579 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000580
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400581 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800582 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500583 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800584
Chris Dalton60283612018-02-14 13:38:14 -0700585 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700586 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500587 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800588
joshualittabb52a12015-01-13 15:02:10 -0800589 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500590 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400591 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
592 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
593 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800594
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400595 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
596 // to compute both the edges because we need two separate test equations for
597 // the single offset.
598 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
599 // the distance by the gradient, non-uniformly scaled by the inverse of the
600 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800601
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400602 // On medium precision devices, we scale the denominator of the distance equation
603 // before taking the inverse square root to minimize the chance that we're dividing
604 // by zero, then we scale the result back.
605
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000606 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400607 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400608 if (egp.fStroke) {
609 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
610 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400611 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
612 if (egp.fUseScale) {
613 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
614 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
615 } else {
616 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
617 }
618 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700619
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000620 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400621 if (args.fShaderCaps->floatIs32Bits()) {
622 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
623 } else {
624 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
625 }
626 if (egp.fUseScale) {
627 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
628 ellipseOffsets.fsIn());
629 } else {
630 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
631 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000632 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000633
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000634 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800635 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400636 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800637 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400638 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400639 if (egp.fUseScale) {
640 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
641 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
642 } else {
643 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
644 }
645 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
646 if (!args.fShaderCaps->floatIs32Bits()) {
647 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
648 }
649 if (egp.fUseScale) {
650 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
651 ellipseOffsets.fsIn());
652 } else {
653 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
654 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000655 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000656 }
657
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400658 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000659 }
660
robertphillips46d36f02015-01-18 08:14:14 -0800661 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500662 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700663 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800664 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400665 uint32_t key = egp.fStroke ? 0x1 : 0x0;
666 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700667 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000668 }
669
bsalomona624bf32016-09-20 09:12:47 -0700670 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500671 const CoordTransformRange& transformRange) override {
bsalomona624bf32016-09-20 09:12:47 -0700672 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400673 this->setTransformDataHelper(pdman, transformRange);
674 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700675 }
676
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000677 private:
egdaniele659a582015-11-13 09:55:43 -0800678 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400679
680 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
681 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000682 };
683
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500684 Attribute fInPosition;
685 Attribute fInColor;
686 Attribute fInEllipseOffset;
687 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400688
joshualitte3ababe2015-05-15 07:56:07 -0700689 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400691 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000692
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400693 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000694
joshualitt249af152014-09-15 11:41:13 -0700695 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696};
697
bsalomoncdaa97b2016-03-08 08:30:14 -0800698GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699
Hal Canary6f6961e2017-01-31 13:50:44 -0500700#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500701GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
702 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
703 d->fRandom->nextBool(), d->fRandom->nextBool(),
704 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000705}
Hal Canary6f6961e2017-01-31 13:50:44 -0500706#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000707
708///////////////////////////////////////////////////////////////////////////////
709
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000710/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000711 * 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 +0000712 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
713 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
714 * using differentials.
715 *
716 * The result is device-independent and can be used with any affine matrix.
717 */
718
bsalomoncdaa97b2016-03-08 08:30:14 -0800719enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000720
bsalomoncdaa97b2016-03-08 08:30:14 -0800721class DIEllipseGeometryProcessor : public GrGeometryProcessor {
722public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500723 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
724 const SkMatrix& viewMatrix, DIEllipseStyle style) {
725 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
726 }
727
728 ~DIEllipseGeometryProcessor() override {}
729
730 const char* name() const override { return "DIEllipseGeometryProcessor"; }
731
732 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
733 GLSLProcessor::GenKey(*this, caps, b);
734 }
735
736 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
737 return new GLSLProcessor();
738 }
739
740private:
741 friend class ::SkArenaAlloc; // for access to ctor
742
Greg Daniel2655ede2019-04-10 00:49:28 +0000743 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400744 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400745 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400746 , fViewMatrix(viewMatrix)
747 , fUseScale(useScale)
748 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500749 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500750 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400751 if (useScale) {
752 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
753 kFloat3_GrSLType};
754 } else {
755 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
756 kFloat2_GrSLType};
757 }
758 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500759 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 }
761
egdaniel57d3b032015-11-13 11:57:27 -0800762 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500764 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000765
joshualitt465283c2015-09-11 08:19:35 -0700766 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800767 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800768 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800769 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800770 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000771
joshualittabb52a12015-01-13 15:02:10 -0800772 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800773 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800774
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400775 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
776 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800777 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500778 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700779
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400780 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800781 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500782 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800783
Chris Dalton60283612018-02-14 13:38:14 -0700784 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500785 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800786
joshualittabb52a12015-01-13 15:02:10 -0800787 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400788 this->writeOutputPosition(vertBuilder,
789 uniformHandler,
790 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500791 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400792 diegp.fViewMatrix,
793 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400794 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800795
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000796 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400797 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
798 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
799 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
800 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400802 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
803 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500804 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400805 if (diegp.fUseScale) {
806 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
807 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000808
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000810 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400811 if (args.fShaderCaps->floatIs32Bits()) {
812 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
813 } else {
814 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
815 }
816 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
817 if (diegp.fUseScale) {
818 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
819 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800820 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000821 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000822 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
823 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000824 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000825 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000826 }
827
828 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800829 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800830 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
831 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400832 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
833 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500834 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
836 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500837 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400838 if (diegp.fUseScale) {
839 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
840 }
841 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
842 if (!args.fShaderCaps->floatIs32Bits()) {
843 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
844 }
845 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
846 if (diegp.fUseScale) {
847 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
848 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000849 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000851
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400852 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000853 }
854
robertphillips46d36f02015-01-18 08:14:14 -0800855 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500856 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700857 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800858 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400859 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
860 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700861 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000862 }
863
bsalomona624bf32016-09-20 09:12:47 -0700864 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
Brian Salomonc241b582019-11-27 08:57:17 -0500865 const CoordTransformRange& transformRange) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800866 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700867
Michael Ludwig553db622020-06-19 10:47:30 -0400868 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
869 this->setTransformDataHelper(pdman, transformRange);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000870 }
871
872 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400873 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700874 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800875
egdaniele659a582015-11-13 09:55:43 -0800876 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000877 };
878
Brian Salomon92be2f72018-06-19 14:33:47 -0400879
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500880 Attribute fInPosition;
881 Attribute fInColor;
882 Attribute fInEllipseOffsets0;
883 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400884
Brian Salomon289e3d82016-12-14 15:52:56 -0500885 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400886 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500887 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400889 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000890
joshualitt249af152014-09-15 11:41:13 -0700891 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000892};
893
bsalomoncdaa97b2016-03-08 08:30:14 -0800894GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000895
Hal Canary6f6961e2017-01-31 13:50:44 -0500896#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500897GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
898 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
899 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
900 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000901}
Hal Canary6f6961e2017-01-31 13:50:44 -0500902#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000903
904///////////////////////////////////////////////////////////////////////////////
905
jvanverth6ca48822016-10-07 06:57:32 -0700906// We have two possible cases for geometry for a circle:
907
908// In the case of a normal fill, we draw geometry for the circle as an octagon.
909static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500910 // enter the octagon
911 // clang-format off
912 0, 1, 8, 1, 2, 8,
913 2, 3, 8, 3, 4, 8,
914 4, 5, 8, 5, 6, 8,
915 6, 7, 8, 7, 0, 8
916 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700917};
918
919// For stroked circles, we use two nested octagons.
920static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500921 // enter the octagon
922 // clang-format off
923 0, 1, 9, 0, 9, 8,
924 1, 2, 10, 1, 10, 9,
925 2, 3, 11, 2, 11, 10,
926 3, 4, 12, 3, 12, 11,
927 4, 5, 13, 4, 13, 12,
928 5, 6, 14, 5, 14, 13,
929 6, 7, 15, 6, 15, 14,
930 7, 0, 8, 7, 8, 15,
931 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700932};
933
Brian Osman9d958b52018-11-13 12:46:56 -0500934// Normalized geometry for octagons that circumscribe and lie on a circle:
935
936static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
937static constexpr SkPoint kOctagonOuter[] = {
938 SkPoint::Make(-kOctOffset, -1),
939 SkPoint::Make( kOctOffset, -1),
940 SkPoint::Make( 1, -kOctOffset),
941 SkPoint::Make( 1, kOctOffset),
942 SkPoint::Make( kOctOffset, 1),
943 SkPoint::Make(-kOctOffset, 1),
944 SkPoint::Make(-1, kOctOffset),
945 SkPoint::Make(-1, -kOctOffset),
946};
947
948// cosine and sine of pi/8
949static constexpr SkScalar kCosPi8 = 0.923579533f;
950static constexpr SkScalar kSinPi8 = 0.382683432f;
951static constexpr SkPoint kOctagonInner[] = {
952 SkPoint::Make(-kSinPi8, -kCosPi8),
953 SkPoint::Make( kSinPi8, -kCosPi8),
954 SkPoint::Make( kCosPi8, -kSinPi8),
955 SkPoint::Make( kCosPi8, kSinPi8),
956 SkPoint::Make( kSinPi8, kCosPi8),
957 SkPoint::Make(-kSinPi8, kCosPi8),
958 SkPoint::Make(-kCosPi8, kSinPi8),
959 SkPoint::Make(-kCosPi8, -kSinPi8),
960};
Brian Salomon289e3d82016-12-14 15:52:56 -0500961
jvanverth6ca48822016-10-07 06:57:32 -0700962static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
963static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
964static const int kVertsPerStrokeCircle = 16;
965static const int kVertsPerFillCircle = 9;
966
967static int circle_type_to_vert_count(bool stroked) {
968 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
969}
970
971static int circle_type_to_index_count(bool stroked) {
972 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
973}
974
975static const uint16_t* circle_type_to_indices(bool stroked) {
976 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
977}
978
979///////////////////////////////////////////////////////////////////////////////
980
Brian Salomon05441c42017-05-15 16:45:49 -0400981class CircleOp final : public GrMeshDrawOp {
982private:
983 using Helper = GrSimpleMeshDrawOpHelper;
984
joshualitt76e7fb62015-02-11 08:52:27 -0800985public:
Brian Salomon25a88092016-12-01 09:36:50 -0500986 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700987
bsalomon4f3a0ca2016-08-22 13:14:26 -0700988 /** Optional extra params to render a partial arc rather than a full circle. */
989 struct ArcParams {
990 SkScalar fStartAngleRadians;
991 SkScalar fSweepAngleRadians;
992 bool fUseCenter;
993 };
Brian Salomon05441c42017-05-15 16:45:49 -0400994
Robert Phillipsb97da532019-02-12 15:24:12 -0500995 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400996 GrPaint&& paint,
997 const SkMatrix& viewMatrix,
998 SkPoint center,
999 SkScalar radius,
1000 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -04001001 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001002 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001003 if (style.hasPathEffect()) {
1004 return nullptr;
1005 }
Brian Salomon05441c42017-05-15 16:45:49 -04001006 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001007 SkStrokeRec::Style recStyle = stroke.getStyle();
1008 if (arcParams) {
1009 // Arc support depends on the style.
1010 switch (recStyle) {
1011 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001012 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001013 return nullptr;
1014 case SkStrokeRec::kFill_Style:
1015 // This supports all fills.
1016 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001017 case SkStrokeRec::kStroke_Style:
1018 // Strokes that don't use the center point are supported with butt and round
1019 // caps.
1020 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1021 return nullptr;
1022 }
1023 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001024 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001025 // Hairline only supports butt cap. Round caps could be emulated by slightly
1026 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001027 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1028 return nullptr;
1029 }
1030 break;
1031 }
1032 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001033 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1034 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001035 }
1036
Greg Daniel2655ede2019-04-10 00:49:28 +00001037 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001038 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1039 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001040 : GrMeshDrawOp(ClassID())
1041 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001042 const SkStrokeRec& stroke = style.strokeRec();
1043 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001044
Brian Salomon45c92202018-04-10 10:53:58 -04001045 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001046
bsalomon4b4a7cc2016-07-08 04:42:54 -07001047 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001048 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001049 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001050
Brian Salomon289e3d82016-12-14 15:52:56 -05001051 bool isStrokeOnly =
1052 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001053 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001054
jvanverth6ca48822016-10-07 06:57:32 -07001055 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001056 SkScalar outerRadius = radius;
1057 SkScalar halfWidth = 0;
1058 if (hasStroke) {
1059 if (SkScalarNearlyZero(strokeWidth)) {
1060 halfWidth = SK_ScalarHalf;
1061 } else {
1062 halfWidth = SkScalarHalf(strokeWidth);
1063 }
1064
1065 outerRadius += halfWidth;
1066 if (isStrokeOnly) {
1067 innerRadius = radius - halfWidth;
1068 }
1069 }
1070
1071 // The radii are outset for two reasons. First, it allows the shader to simply perform
1072 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1073 // Second, the outer radius is used to compute the verts of the bounding box that is
1074 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001075 outerRadius += SK_ScalarHalf;
1076 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001077 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001078 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001079
bsalomon4f3a0ca2016-08-22 13:14:26 -07001080 // This makes every point fully inside the intersection plane.
1081 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1082 // This makes every point fully outside the union plane.
1083 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001084 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001085 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1086 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001087 if (arcParams) {
1088 // The shader operates in a space where the circle is translated to be centered at the
1089 // origin. Here we compute points on the unit circle at the starting and ending angles.
1090 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001091 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1092 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001093 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001094 stopPoint.fY = SkScalarSin(endAngle);
1095 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001096
1097 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1098 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1099 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1100 startPoint.normalize();
1101 stopPoint.normalize();
1102
Brian Salomon3517aa72019-12-11 08:16:22 -05001103 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1104 // should orient the clip planes for arcs.
1105 SkASSERT(viewMatrix.isSimilarity());
1106 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1107 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1108 if (upperLeftDet < 0) {
1109 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001110 }
1111
Brian Salomon45c92202018-04-10 10:53:58 -04001112 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1113 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1114 SkPoint roundCaps[2];
1115 if (fRoundCaps) {
1116 // Compute the cap center points in the normalized space.
1117 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1118 roundCaps[0] = startPoint * midRadius;
1119 roundCaps[1] = stopPoint * midRadius;
1120 } else {
1121 roundCaps[0] = kUnusedRoundCaps[0];
1122 roundCaps[1] = kUnusedRoundCaps[1];
1123 }
1124
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001126 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1127 // center of the butts.
1128 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001129 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001130 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001131 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001132 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1133 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1134 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001135 if (useCenter) {
1136 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1137 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001138 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1139 if (arcParams->fSweepAngleRadians < 0) {
1140 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001141 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001142 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001143 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001144 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001145 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001146 color,
1147 innerRadius,
1148 outerRadius,
1149 {norm0.fX, norm0.fY, 0.5f},
1150 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1151 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001152 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001153 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001154 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001155 fClipPlaneIsect = false;
1156 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001157 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001158 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001159 color,
1160 innerRadius,
1161 outerRadius,
1162 {norm0.fX, norm0.fY, 0.5f},
1163 {norm1.fX, norm1.fY, 0.5f},
1164 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001165 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001166 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001167 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001168 fClipPlaneIsect = true;
1169 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001170 }
1171 } else {
1172 // We clip to a secant of the original circle.
1173 startPoint.scale(radius);
1174 stopPoint.scale(radius);
1175 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1176 norm.normalize();
1177 if (arcParams->fSweepAngleRadians > 0) {
1178 norm.negate();
1179 }
1180 SkScalar d = -norm.dot(startPoint) + 0.5f;
1181
Brian Salomon05441c42017-05-15 16:45:49 -04001182 fCircles.emplace_back(
1183 Circle{color,
1184 innerRadius,
1185 outerRadius,
1186 {norm.fX, norm.fY, d},
1187 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1188 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001189 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001190 devBounds,
1191 stroked});
1192 fClipPlane = true;
1193 fClipPlaneIsect = false;
1194 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 }
1196 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001197 fCircles.emplace_back(
1198 Circle{color,
1199 innerRadius,
1200 outerRadius,
1201 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1202 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1203 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001204 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001205 devBounds,
1206 stroked});
1207 fClipPlane = false;
1208 fClipPlaneIsect = false;
1209 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001210 }
bsalomon88cf17d2016-07-08 06:40:56 -07001211 // Use the original radius and stroke radius for the bounds so that it does not include the
1212 // AA bloat.
1213 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001214 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001215 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001216 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001217 fVertCount = circle_type_to_vert_count(stroked);
1218 fIndexCount = circle_type_to_index_count(stroked);
1219 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001220 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001221
Brian Salomon289e3d82016-12-14 15:52:56 -05001222 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001223
Chris Dalton1706cbf2019-05-21 19:35:29 -06001224 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001225 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001226 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001227 } else {
1228 fHelper.visitProxies(func);
1229 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001230 }
1231
Brian Osman9a390ac2018-11-12 09:47:48 -05001232#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001233 SkString dumpInfo() const override {
1234 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001235 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001236 string.appendf(
1237 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1238 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001239 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001240 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1241 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1242 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001243 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001244 string += fHelper.dumpInfo();
1245 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001246 return string;
1247 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001248#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001249
Chris Dalton6ce447a2019-06-23 18:07:38 -06001250 GrProcessorSet::Analysis finalize(
1251 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1252 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001253 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001254 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001255 GrProcessorAnalysisCoverage::kSingleChannel, color,
1256 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001257 }
1258
1259 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1260
bsalomone46f9fe2015-08-18 06:05:14 -07001261private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001262 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001263
Robert Phillips4133dc42020-03-11 15:55:55 -04001264 void onCreateProgramInfo(const GrCaps* caps,
1265 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001266 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001267 GrAppliedClip&& appliedClip,
1268 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001269 SkMatrix localMatrix;
1270 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001271 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001272 }
1273
Robert Phillips4490d922020-03-03 14:50:59 -05001274 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001275 fClipPlaneIsect, fClipPlaneUnion,
1276 fRoundCaps, fWideColor,
1277 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001278
Brian Salomon8afde5f2020-04-01 16:22:00 -04001279 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001280 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001281 }
1282
Robert Phillips4490d922020-03-03 14:50:59 -05001283 void onPrepareDraws(Target* target) override {
1284 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001285 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001286 if (!fProgramInfo) {
1287 return;
1288 }
1289 }
1290
Brian Salomon12d22642019-01-29 14:38:50 -05001291 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001292 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001293 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1294 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001295 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001296 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001297 return;
1298 }
1299
Brian Salomon12d22642019-01-29 14:38:50 -05001300 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001301 int firstIndex = 0;
1302 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1303 if (!indices) {
1304 SkDebugf("Could not allocate indices\n");
1305 return;
1306 }
1307
1308 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001309 for (const auto& circle : fCircles) {
1310 SkScalar innerRadius = circle.fInnerRadius;
1311 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001312 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001313 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001314
joshualitt76e7fb62015-02-11 08:52:27 -08001315 // The inner radius in the vertex data must be specified in normalized space.
1316 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001317 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001318
1319 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001320 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001321
Brian Osman9a24fee2018-08-03 09:48:42 -04001322 SkVector geoClipPlane = { 0, 0 };
1323 SkScalar offsetClipDist = SK_Scalar1;
1324 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1325 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1326 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1327 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1328 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1329 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1330 // the AA can extend just past the center of the circle.
1331 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1332 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1333 SkAssertResult(geoClipPlane.normalize());
1334 offsetClipDist = 0.5f / halfWidth;
1335 }
1336
Brian Osman7d8f82b2018-11-08 10:24:09 -05001337 for (int i = 0; i < 8; ++i) {
1338 // This clips the normalized offset to the half-plane we computed above. Then we
1339 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001340 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001341 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001342 vertices.write(center + offset * halfWidth,
1343 color,
1344 offset,
1345 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001346 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001347 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 }
1349 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001350 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001351 }
1352 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001353 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001354 }
1355 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 }
Brian Salomon45c92202018-04-10 10:53:58 -04001358 }
jvanverth6ca48822016-10-07 06:57:32 -07001359
Brian Salomon05441c42017-05-15 16:45:49 -04001360 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001361 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001362
Brian Osman7d8f82b2018-11-08 10:24:09 -05001363 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1365 color,
1366 kOctagonInner[i] * innerRadius,
1367 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001368 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001370 }
1371 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001372 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001373 }
1374 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001375 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001376 }
1377 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001378 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001379 }
Brian Salomon45c92202018-04-10 10:53:58 -04001380 }
jvanverth6ca48822016-10-07 06:57:32 -07001381 } else {
1382 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001383 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001384 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001385 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001386 }
jvanverth6ca48822016-10-07 06:57:32 -07001387 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001388 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001389 }
1390 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001391 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001392 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001393 if (fRoundCaps) {
1394 vertices.write(circle.fRoundCapCenters);
1395 }
jvanverth6ca48822016-10-07 06:57:32 -07001396 }
1397
Brian Salomon05441c42017-05-15 16:45:49 -04001398 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1399 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001400 for (int i = 0; i < primIndexCount; ++i) {
1401 *indices++ = primIndices[i] + currStartVertex;
1402 }
1403
Brian Salomon05441c42017-05-15 16:45:49 -04001404 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001405 }
jvanverth6ca48822016-10-07 06:57:32 -07001406
Robert Phillips4490d922020-03-03 14:50:59 -05001407 fMesh = target->allocMesh();
1408 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001409 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001410 }
1411
1412 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001413 if (!fProgramInfo || !fMesh) {
1414 return;
1415 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001416
Chris Dalton765ed362020-03-16 17:34:44 -06001417 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1418 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1419 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001420 }
1421
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001422 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1423 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001424 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001425
1426 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001427 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001428 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001429 }
1430
Brian Salomon05441c42017-05-15 16:45:49 -04001431 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001432 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001433 }
1434
Brian Salomon05441c42017-05-15 16:45:49 -04001435 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001436 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1437 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001438 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001439 }
1440
Brian Salomon289e3d82016-12-14 15:52:56 -05001441 // Because we've set up the ops that don't use the planes with noop values
1442 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001443 fClipPlane |= that->fClipPlane;
1444 fClipPlaneIsect |= that->fClipPlaneIsect;
1445 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001446 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001447 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001448
Brian Salomon05441c42017-05-15 16:45:49 -04001449 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001450 fVertCount += that->fVertCount;
1451 fIndexCount += that->fIndexCount;
1452 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001453 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001454 }
1455
Brian Salomon05441c42017-05-15 16:45:49 -04001456 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001457 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001458 SkScalar fInnerRadius;
1459 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001460 SkScalar fClipPlane[3];
1461 SkScalar fIsectPlane[3];
1462 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001463 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001464 SkRect fDevBounds;
1465 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001466 };
1467
Brian Salomon289e3d82016-12-14 15:52:56 -05001468 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001469 Helper fHelper;
1470 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001471 int fVertCount;
1472 int fIndexCount;
1473 bool fAllFill;
1474 bool fClipPlane;
1475 bool fClipPlaneIsect;
1476 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001477 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001478 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001479
Chris Daltoneb694b72020-03-16 09:25:50 -06001480 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001481 GrProgramInfo* fProgramInfo = nullptr;
1482
Brian Salomon05441c42017-05-15 16:45:49 -04001483 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001484};
1485
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001486class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1487private:
1488 using Helper = GrSimpleMeshDrawOpHelper;
1489
1490public:
1491 DEFINE_OP_CLASS_ID
1492
Robert Phillipsb97da532019-02-12 15:24:12 -05001493 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001494 GrPaint&& paint,
1495 const SkMatrix& viewMatrix,
1496 SkPoint center,
1497 SkScalar radius,
1498 SkScalar strokeWidth,
1499 SkScalar startAngle,
1500 SkScalar onAngle,
1501 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001502 SkScalar phaseAngle) {
1503 SkASSERT(circle_stays_circle(viewMatrix));
1504 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001505 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1506 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001507 onAngle, offAngle, phaseAngle);
1508 }
1509
Brian Osmancf860852018-10-31 14:04:39 -04001510 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001511 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1512 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1513 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001514 : GrMeshDrawOp(ClassID())
1515 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001516 SkASSERT(circle_stays_circle(viewMatrix));
1517 viewMatrix.mapPoints(&center, 1);
1518 radius = viewMatrix.mapRadius(radius);
1519 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1520
1521 // Determine the angle where the circle starts in device space and whether its orientation
1522 // has been reversed.
1523 SkVector start;
1524 bool reflection;
1525 if (!startAngle) {
1526 start = {1, 0};
1527 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001528 start.fY = SkScalarSin(startAngle);
1529 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001530 }
1531 viewMatrix.mapVectors(&start, 1);
1532 startAngle = SkScalarATan2(start.fY, start.fX);
1533 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1534 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1535
1536 auto totalAngle = onAngle + offAngle;
1537 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1538
1539 SkScalar halfWidth = 0;
1540 if (SkScalarNearlyZero(strokeWidth)) {
1541 halfWidth = SK_ScalarHalf;
1542 } else {
1543 halfWidth = SkScalarHalf(strokeWidth);
1544 }
1545
1546 SkScalar outerRadius = radius + halfWidth;
1547 SkScalar innerRadius = radius - halfWidth;
1548
1549 // The radii are outset for two reasons. First, it allows the shader to simply perform
1550 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1551 // Second, the outer radius is used to compute the verts of the bounding box that is
1552 // rendered and the outset ensures the box will cover all partially covered by the circle.
1553 outerRadius += SK_ScalarHalf;
1554 innerRadius -= SK_ScalarHalf;
1555 fViewMatrixIfUsingLocalCoords = viewMatrix;
1556
1557 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1558 center.fX + outerRadius, center.fY + outerRadius);
1559
1560 // We store whether there is a reflection as a negative total angle.
1561 if (reflection) {
1562 totalAngle = -totalAngle;
1563 }
1564 fCircles.push_back(Circle{
1565 color,
1566 outerRadius,
1567 innerRadius,
1568 onAngle,
1569 totalAngle,
1570 startAngle,
1571 phaseAngle,
1572 devBounds
1573 });
1574 // Use the original radius and stroke radius for the bounds so that it does not include the
1575 // AA bloat.
1576 radius += halfWidth;
1577 this->setBounds(
1578 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001579 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001580 fVertCount = circle_type_to_vert_count(true);
1581 fIndexCount = circle_type_to_index_count(true);
1582 }
1583
1584 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1585
Chris Dalton1706cbf2019-05-21 19:35:29 -06001586 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001587 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001588 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001589 } else {
1590 fHelper.visitProxies(func);
1591 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001592 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001593
Brian Osman9a390ac2018-11-12 09:47:48 -05001594#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001595 SkString dumpInfo() const override {
1596 SkString string;
1597 for (int i = 0; i < fCircles.count(); ++i) {
1598 string.appendf(
1599 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1600 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1601 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001602 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001603 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1604 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1605 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1606 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001607 }
1608 string += fHelper.dumpInfo();
1609 string += INHERITED::dumpInfo();
1610 return string;
1611 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001612#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001613
Chris Dalton6ce447a2019-06-23 18:07:38 -06001614 GrProcessorSet::Analysis finalize(
1615 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1616 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001617 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001618 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001619 GrProcessorAnalysisCoverage::kSingleChannel, color,
1620 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001621 }
1622
1623 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1624
1625private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001626 GrProgramInfo* programInfo() override { return fProgramInfo; }
1627
Robert Phillips4133dc42020-03-11 15:55:55 -04001628 void onCreateProgramInfo(const GrCaps* caps,
1629 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001630 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001631 GrAppliedClip&& appliedClip,
1632 const GrXferProcessor::DstProxyView& dstProxyView) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001633 SkMatrix localMatrix;
1634 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001635 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001636 }
1637
1638 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001639 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001640 fWideColor,
1641 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001642
Brian Salomon8afde5f2020-04-01 16:22:00 -04001643 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001644 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001645 }
1646
Robert Phillips4490d922020-03-03 14:50:59 -05001647 void onPrepareDraws(Target* target) override {
1648 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001649 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001650 if (!fProgramInfo) {
1651 return;
1652 }
1653 }
1654
Brian Salomon12d22642019-01-29 14:38:50 -05001655 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001656 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001657 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1658 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001659 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 SkDebugf("Could not allocate vertices\n");
1661 return;
1662 }
1663
Brian Salomon12d22642019-01-29 14:38:50 -05001664 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001665 int firstIndex = 0;
1666 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1667 if (!indices) {
1668 SkDebugf("Could not allocate indices\n");
1669 return;
1670 }
1671
1672 int currStartVertex = 0;
1673 for (const auto& circle : fCircles) {
1674 // The inner radius in the vertex data must be specified in normalized space so that
1675 // length() can be called with smaller values to avoid precision issues with half
1676 // floats.
1677 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1678 const SkRect& bounds = circle.fDevBounds;
1679 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001680 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1681 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1682 };
1683 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001684 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001685 dashParams.totalAngle = -dashParams.totalAngle;
1686 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001687 }
1688
Brian Osmane3caf2d2018-11-21 13:48:36 -05001689 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001690
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001691 // The bounding geometry for the circle is composed of an outer bounding octagon and
1692 // an inner bounded octagon.
1693
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001694 // Compute the vertices of the outer octagon.
1695 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1696 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001697
1698 auto reflectY = [=](const SkPoint& p) {
1699 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700 };
Brian Osman9d958b52018-11-13 12:46:56 -05001701
1702 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001703 vertices.write(center + kOctagonOuter[i] * halfWidth,
1704 color,
1705 reflectY(kOctagonOuter[i]),
1706 circle.fOuterRadius,
1707 normInnerRadius,
1708 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001709 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001710
1711 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001712 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001713 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1714 color,
1715 reflectY(kOctagonInner[i]) * normInnerRadius,
1716 circle.fOuterRadius,
1717 normInnerRadius,
1718 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001719 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001720
1721 const uint16_t* primIndices = circle_type_to_indices(true);
1722 const int primIndexCount = circle_type_to_index_count(true);
1723 for (int i = 0; i < primIndexCount; ++i) {
1724 *indices++ = primIndices[i] + currStartVertex;
1725 }
1726
1727 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001728 }
1729
Robert Phillips4490d922020-03-03 14:50:59 -05001730 fMesh = target->allocMesh();
1731 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001732 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001733 }
1734
1735 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001736 if (!fProgramInfo || !fMesh) {
1737 return;
1738 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001739
Chris Dalton765ed362020-03-16 17:34:44 -06001740 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1741 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1742 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001743 }
1744
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001745 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1746 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001747 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1748
1749 // can only represent 65535 unique vertices with 16-bit indices
1750 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001751 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001752 }
1753
1754 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001755 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001756 }
1757
1758 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001759 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1760 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001761 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001762 }
1763
1764 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001765 fVertCount += that->fVertCount;
1766 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001767 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001768 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001769 }
1770
1771 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001772 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001773 SkScalar fOuterRadius;
1774 SkScalar fInnerRadius;
1775 SkScalar fOnAngle;
1776 SkScalar fTotalAngle;
1777 SkScalar fStartAngle;
1778 SkScalar fPhaseAngle;
1779 SkRect fDevBounds;
1780 };
1781
1782 SkMatrix fViewMatrixIfUsingLocalCoords;
1783 Helper fHelper;
1784 SkSTArray<1, Circle, true> fCircles;
1785 int fVertCount;
1786 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001787 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001788
Chris Daltoneb694b72020-03-16 09:25:50 -06001789 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001790 GrProgramInfo* fProgramInfo = nullptr;
1791
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001792 typedef GrMeshDrawOp INHERITED;
1793};
1794
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001795///////////////////////////////////////////////////////////////////////////////
1796
Brian Salomon05441c42017-05-15 16:45:49 -04001797class EllipseOp : public GrMeshDrawOp {
1798private:
1799 using Helper = GrSimpleMeshDrawOpHelper;
1800
1801 struct DeviceSpaceParams {
1802 SkPoint fCenter;
1803 SkScalar fXRadius;
1804 SkScalar fYRadius;
1805 SkScalar fInnerXRadius;
1806 SkScalar fInnerYRadius;
1807 };
1808
joshualitt76e7fb62015-02-11 08:52:27 -08001809public:
Brian Salomon25a88092016-12-01 09:36:50 -05001810 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001811
Robert Phillipsb97da532019-02-12 15:24:12 -05001812 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001813 GrPaint&& paint,
1814 const SkMatrix& viewMatrix,
1815 const SkRect& ellipse,
1816 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001817 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001818 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001819 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1820 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001821 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1822 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001823 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1824 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1825 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1826 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001827
bsalomon4b4a7cc2016-07-08 04:42:54 -07001828 // do (potentially) anisotropic mapping of stroke
1829 SkVector scaledStroke;
1830 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001831 scaledStroke.fX = SkScalarAbs(
1832 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1833 scaledStroke.fY = SkScalarAbs(
1834 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001835
1836 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001837 bool isStrokeOnly =
1838 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001839 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1840
Brian Salomon05441c42017-05-15 16:45:49 -04001841 params.fInnerXRadius = 0;
1842 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001843 if (hasStroke) {
1844 if (SkScalarNearlyZero(scaledStroke.length())) {
1845 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1846 } else {
1847 scaledStroke.scale(SK_ScalarHalf);
1848 }
1849
1850 // we only handle thick strokes for near-circular ellipses
1851 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001852 (0.5f * params.fXRadius > params.fYRadius ||
1853 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001854 return nullptr;
1855 }
1856
1857 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001858 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1859 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1860 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1861 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001862 return nullptr;
1863 }
1864
1865 // this is legit only if scale & translation (which should be the case at the moment)
1866 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001867 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1868 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869 }
1870
Brian Salomon05441c42017-05-15 16:45:49 -04001871 params.fXRadius += scaledStroke.fX;
1872 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001874
1875 // For large ovals with low precision floats, we fall back to the path renderer.
1876 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1877 // minimum value to avoid divides by zero. With large ovals and low precision this
1878 // leads to blurring at the edge of the oval.
1879 const SkScalar kMaxOvalRadius = 16384;
1880 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1881 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1882 return nullptr;
1883 }
1884
Greg Daniel2655ede2019-04-10 00:49:28 +00001885 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001886 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001887 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001888
Brian Osmancf860852018-10-31 14:04:39 -04001889 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001890 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001891 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001892 : INHERITED(ClassID())
1893 , fHelper(helperArgs, GrAAType::kCoverage)
1894 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001895 SkStrokeRec::Style style = stroke.getStyle();
1896 bool isStrokeOnly =
1897 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001898
Brian Salomon05441c42017-05-15 16:45:49 -04001899 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1900 params.fInnerXRadius, params.fInnerYRadius,
1901 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1902 params.fCenter.fY - params.fYRadius,
1903 params.fCenter.fX + params.fXRadius,
1904 params.fCenter.fY + params.fYRadius)});
1905
Greg Daniel5faf4742019-10-01 15:14:44 -04001906 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001907
bsalomon4b4a7cc2016-07-08 04:42:54 -07001908 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001909 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001910
Brian Salomon05441c42017-05-15 16:45:49 -04001911 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1912 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001913 }
joshualitt76e7fb62015-02-11 08:52:27 -08001914
Brian Salomon289e3d82016-12-14 15:52:56 -05001915 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001916
Chris Dalton1706cbf2019-05-21 19:35:29 -06001917 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001918 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001919 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001920 } else {
1921 fHelper.visitProxies(func);
1922 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001923 }
1924
Brian Osman9a390ac2018-11-12 09:47:48 -05001925#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001926 SkString dumpInfo() const override {
1927 SkString string;
1928 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001929 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001930 string.appendf(
1931 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1932 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001933 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001934 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1935 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001936 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001937 string += fHelper.dumpInfo();
1938 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001939 return string;
1940 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001941#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001942
Chris Dalton6ce447a2019-06-23 18:07:38 -06001943 GrProcessorSet::Analysis finalize(
1944 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1945 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001946 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1947 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001948 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001949 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001950 GrProcessorAnalysisCoverage::kSingleChannel, color,
1951 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001952 }
1953
1954 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1955
bsalomone46f9fe2015-08-18 06:05:14 -07001956private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001957 GrProgramInfo* programInfo() override { return fProgramInfo; }
1958
Robert Phillips4133dc42020-03-11 15:55:55 -04001959 void onCreateProgramInfo(const GrCaps* caps,
1960 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001961 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001962 GrAppliedClip&& appliedClip,
1963 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001964 SkMatrix localMatrix;
1965 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001966 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001967 }
1968
Robert Phillips4490d922020-03-03 14:50:59 -05001969 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1970 fUseScale, localMatrix);
1971
Brian Salomon8afde5f2020-04-01 16:22:00 -04001972 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001973 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001974 }
1975
Robert Phillips4490d922020-03-03 14:50:59 -05001976 void onPrepareDraws(Target* target) override {
1977 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001978 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001979 if (!fProgramInfo) {
1980 return;
1981 }
1982 }
1983
1984 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001985 GrVertexWriter verts{helper.vertices()};
1986 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001987 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001988 return;
1989 }
1990
Brian Salomon05441c42017-05-15 16:45:49 -04001991 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001992 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001993 SkScalar xRadius = ellipse.fXRadius;
1994 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001995
1996 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001997 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1998 SkScalarInvert(xRadius),
1999 SkScalarInvert(yRadius),
2000 SkScalarInvert(ellipse.fInnerXRadius),
2001 SkScalarInvert(ellipse.fInnerYRadius)
2002 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002003 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2004 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002005
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002006 if (!fStroked) {
2007 // For filled ellipses we map a unit circle in the vertex attributes rather than
2008 // computing an ellipse and modifying that distance, so we normalize to 1
2009 xMaxOffset /= xRadius;
2010 yMaxOffset /= yRadius;
2011 }
2012
joshualitt76e7fb62015-02-11 08:52:27 -08002013 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002014 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2015 color,
2016 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002017 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002018 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002019 }
Robert Phillips4490d922020-03-03 14:50:59 -05002020 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002021 }
2022
2023 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002024 if (!fProgramInfo || !fMesh) {
2025 return;
2026 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002027
Chris Dalton765ed362020-03-16 17:34:44 -06002028 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2029 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2030 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002031 }
2032
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002033 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2034 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002035 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002036
Brian Salomon05441c42017-05-15 16:45:49 -04002037 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002038 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002039 }
2040
bsalomoncdaa97b2016-03-08 08:30:14 -08002041 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002042 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002043 }
2044
Brian Salomon05441c42017-05-15 16:45:49 -04002045 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002046 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2047 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002048 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002049 }
2050
Brian Salomon05441c42017-05-15 16:45:49 -04002051 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002052 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002053 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002054 }
2055
Brian Salomon05441c42017-05-15 16:45:49 -04002056 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002057 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002058 SkScalar fXRadius;
2059 SkScalar fYRadius;
2060 SkScalar fInnerXRadius;
2061 SkScalar fInnerYRadius;
2062 SkRect fDevBounds;
2063 };
joshualitt76e7fb62015-02-11 08:52:27 -08002064
Brian Salomon289e3d82016-12-14 15:52:56 -05002065 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002066 Helper fHelper;
2067 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002068 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002069 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002070 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002071
Chris Daltoneb694b72020-03-16 09:25:50 -06002072 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002073 GrProgramInfo* fProgramInfo = nullptr;
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002076};
2077
joshualitt76e7fb62015-02-11 08:52:27 -08002078/////////////////////////////////////////////////////////////////////////////////////////////////
2079
Brian Salomon05441c42017-05-15 16:45:49 -04002080class DIEllipseOp : public GrMeshDrawOp {
2081private:
2082 using Helper = GrSimpleMeshDrawOpHelper;
2083
2084 struct DeviceSpaceParams {
2085 SkPoint fCenter;
2086 SkScalar fXRadius;
2087 SkScalar fYRadius;
2088 SkScalar fInnerXRadius;
2089 SkScalar fInnerYRadius;
2090 DIEllipseStyle fStyle;
2091 };
2092
joshualitt76e7fb62015-02-11 08:52:27 -08002093public:
Brian Salomon25a88092016-12-01 09:36:50 -05002094 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002095
Robert Phillipsb97da532019-02-12 15:24:12 -05002096 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002097 GrPaint&& paint,
2098 const SkMatrix& viewMatrix,
2099 const SkRect& ellipse,
2100 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002101 DeviceSpaceParams params;
2102 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2103 params.fXRadius = SkScalarHalf(ellipse.width());
2104 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002105
bsalomon4b4a7cc2016-07-08 04:42:54 -07002106 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002107 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2108 ? DIEllipseStyle::kStroke
2109 : (SkStrokeRec::kHairline_Style == style)
2110 ? DIEllipseStyle::kHairline
2111 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002112
Brian Salomon05441c42017-05-15 16:45:49 -04002113 params.fInnerXRadius = 0;
2114 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002115 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2116 SkScalar strokeWidth = stroke.getWidth();
2117
2118 if (SkScalarNearlyZero(strokeWidth)) {
2119 strokeWidth = SK_ScalarHalf;
2120 } else {
2121 strokeWidth *= SK_ScalarHalf;
2122 }
2123
2124 // we only handle thick strokes for near-circular ellipses
2125 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002126 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2127 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002128 return nullptr;
2129 }
2130
2131 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002132 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2133 (strokeWidth * strokeWidth) * params.fXRadius) {
2134 return nullptr;
2135 }
2136 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2137 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002138 return nullptr;
2139 }
2140
2141 // set inner radius (if needed)
2142 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002143 params.fInnerXRadius = params.fXRadius - strokeWidth;
2144 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002145 }
2146
Brian Salomon05441c42017-05-15 16:45:49 -04002147 params.fXRadius += strokeWidth;
2148 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002149 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002150
2151 // For large ovals with low precision floats, we fall back to the path renderer.
2152 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2153 // minimum value to avoid divides by zero. With large ovals and low precision this
2154 // leads to blurring at the edge of the oval.
2155 const SkScalar kMaxOvalRadius = 16384;
2156 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2157 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2158 return nullptr;
2159 }
2160
Brian Salomon05441c42017-05-15 16:45:49 -04002161 if (DIEllipseStyle::kStroke == params.fStyle &&
2162 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2163 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002164 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002165 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002166 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002167
Greg Daniel2655ede2019-04-10 00:49:28 +00002168 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002169 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002170 : INHERITED(ClassID())
2171 , fHelper(helperArgs, GrAAType::kCoverage)
2172 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002173 // This expands the outer rect so that after CTM we end up with a half-pixel border
2174 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2175 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2176 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2177 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2178 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2179 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002180
Brian Salomon05441c42017-05-15 16:45:49 -04002181 fEllipses.emplace_back(
2182 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2183 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2184 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2185 params.fCenter.fY - params.fYRadius - geoDy,
2186 params.fCenter.fX + params.fXRadius + geoDx,
2187 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002188 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002189 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002190 }
2191
Brian Salomon289e3d82016-12-14 15:52:56 -05002192 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002193
Chris Dalton1706cbf2019-05-21 19:35:29 -06002194 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002195 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002196 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002197 } else {
2198 fHelper.visitProxies(func);
2199 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002200 }
2201
Brian Osman9a390ac2018-11-12 09:47:48 -05002202#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002203 SkString dumpInfo() const override {
2204 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002205 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002206 string.appendf(
2207 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2208 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2209 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002210 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002211 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2212 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002213 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002214 string += fHelper.dumpInfo();
2215 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002216 return string;
2217 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002218#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002219
Chris Dalton6ce447a2019-06-23 18:07:38 -06002220 GrProcessorSet::Analysis finalize(
2221 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2222 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002223 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2224 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002225 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002226 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002227 GrProcessorAnalysisCoverage::kSingleChannel, color,
2228 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002229 }
2230
2231 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2232
bsalomone46f9fe2015-08-18 06:05:14 -07002233private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002234 GrProgramInfo* programInfo() override { return fProgramInfo; }
2235
Robert Phillips4133dc42020-03-11 15:55:55 -04002236 void onCreateProgramInfo(const GrCaps* caps,
2237 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002238 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002239 GrAppliedClip&& appliedClip,
2240 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002241 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2242 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002243 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002244
Brian Salomon8afde5f2020-04-01 16:22:00 -04002245 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002246 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002247 }
2248
Robert Phillips4490d922020-03-03 14:50:59 -05002249 void onPrepareDraws(Target* target) override {
2250 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002251 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002252 }
2253
2254 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002255 GrVertexWriter verts{helper.vertices()};
2256 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002257 return;
2258 }
2259
Brian Salomon05441c42017-05-15 16:45:49 -04002260 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002261 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002262 SkScalar xRadius = ellipse.fXRadius;
2263 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002264
joshualitt76e7fb62015-02-11 08:52:27 -08002265 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002266 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2267 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002268
Brian Osman9d958b52018-11-13 12:46:56 -05002269 // By default, constructed so that inner offset is (0, 0) for all points
2270 SkScalar innerRatioX = -offsetDx;
2271 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002272
Brian Osman9d958b52018-11-13 12:46:56 -05002273 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002274 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002275 innerRatioX = xRadius / ellipse.fInnerXRadius;
2276 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002277 }
joshualitt76e7fb62015-02-11 08:52:27 -08002278
Brian Osman2b6e3902018-11-21 15:29:43 -05002279 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2280 color,
2281 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002282 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002283 origin_centered_tri_strip(innerRatioX + offsetDx,
2284 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002285 }
Robert Phillips4490d922020-03-03 14:50:59 -05002286 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002287 }
2288
2289 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002290 if (!fProgramInfo || !fMesh) {
2291 return;
2292 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002293
Chris Dalton765ed362020-03-16 17:34:44 -06002294 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2295 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2296 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002297 }
halcanary9d524f22016-03-29 09:03:52 -07002298
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002299 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2300 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002301 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002302 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002303 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002304 }
2305
bsalomoncdaa97b2016-03-08 08:30:14 -08002306 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002307 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002308 }
2309
joshualittd96a67b2015-05-05 14:09:05 -07002310 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002311 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002312 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002313 }
2314
Brian Salomon05441c42017-05-15 16:45:49 -04002315 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002316 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002317 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002318 }
2319
Brian Salomon05441c42017-05-15 16:45:49 -04002320 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2321 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002322
Brian Salomon05441c42017-05-15 16:45:49 -04002323 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002324 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002325 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002326 SkScalar fXRadius;
2327 SkScalar fYRadius;
2328 SkScalar fInnerXRadius;
2329 SkScalar fInnerYRadius;
2330 SkScalar fGeoDx;
2331 SkScalar fGeoDy;
2332 DIEllipseStyle fStyle;
2333 SkRect fBounds;
2334 };
2335
Brian Salomon05441c42017-05-15 16:45:49 -04002336 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002337 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002338 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002339 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002340
Chris Daltoneb694b72020-03-16 09:25:50 -06002341 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002342 GrProgramInfo* fProgramInfo = nullptr;
2343
Brian Salomon05441c42017-05-15 16:45:49 -04002344 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002345};
2346
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002347///////////////////////////////////////////////////////////////////////////////
2348
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002349// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002350//
2351// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2352// ____________
2353// |_|________|_|
2354// | | | |
2355// | | | |
2356// | | | |
2357// |_|________|_|
2358// |_|________|_|
2359//
2360// For strokes, we don't draw the center quad.
2361//
2362// For circular roundrects, in the case where the stroke width is greater than twice
2363// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002364// in the center. The shared vertices are duplicated so we can set a different outer radius
2365// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002366// ____________
2367// |_|________|_|
2368// | |\ ____ /| |
2369// | | | | | |
2370// | | |____| | |
2371// |_|/______\|_|
2372// |_|________|_|
2373//
2374// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002375//
2376// For filled rrects that need to provide a distance vector we resuse the overstroke
2377// geometry but make the inner rect degenerate (either a point or a horizontal or
2378// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002379
jvanverth84839f62016-08-29 10:16:40 -07002380static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002381 // clang-format off
2382 // overstroke quads
2383 // we place this at the beginning so that we can skip these indices when rendering normally
2384 16, 17, 19, 16, 19, 18,
2385 19, 17, 23, 19, 23, 21,
2386 21, 23, 22, 21, 22, 20,
2387 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002388
Brian Salomon289e3d82016-12-14 15:52:56 -05002389 // corners
2390 0, 1, 5, 0, 5, 4,
2391 2, 3, 7, 2, 7, 6,
2392 8, 9, 13, 8, 13, 12,
2393 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002394
Brian Salomon289e3d82016-12-14 15:52:56 -05002395 // edges
2396 1, 2, 6, 1, 6, 5,
2397 4, 5, 9, 4, 9, 8,
2398 6, 7, 11, 6, 11, 10,
2399 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002400
Brian Salomon289e3d82016-12-14 15:52:56 -05002401 // center
2402 // we place this at the end so that we can ignore these indices when not rendering as filled
2403 5, 6, 10, 5, 10, 9,
2404 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002405};
Brian Salomon289e3d82016-12-14 15:52:56 -05002406
jvanverth84839f62016-08-29 10:16:40 -07002407// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002408static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409
jvanverth84839f62016-08-29 10:16:40 -07002410// overstroke count is arraysize minus the center indices
2411static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2412// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002413static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002414// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002415static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2416static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002417static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002418
jvanverthc3d0e422016-08-25 08:12:35 -07002419enum RRectType {
2420 kFill_RRectType,
2421 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002422 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002423};
2424
jvanverth84839f62016-08-29 10:16:40 -07002425static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002426 switch (type) {
2427 case kFill_RRectType:
2428 case kStroke_RRectType:
2429 return kVertsPerStandardRRect;
2430 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002431 return kVertsPerOverstrokeRRect;
2432 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002433 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002434}
2435
2436static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002437 switch (type) {
2438 case kFill_RRectType:
2439 return kIndicesPerFillRRect;
2440 case kStroke_RRectType:
2441 return kIndicesPerStrokeRRect;
2442 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002443 return kIndicesPerOverstrokeRRect;
2444 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002445 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002446}
2447
2448static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002449 switch (type) {
2450 case kFill_RRectType:
2451 case kStroke_RRectType:
2452 return gStandardRRectIndices;
2453 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002454 return gOverstrokeRRectIndices;
2455 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002456 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002457}
2458
joshualitt76e7fb62015-02-11 08:52:27 -08002459///////////////////////////////////////////////////////////////////////////////////////////////////
2460
Robert Phillips79839d42016-10-06 15:03:34 -04002461// For distance computations in the interior of filled rrects we:
2462//
2463// add a interior degenerate (point or line) rect
2464// each vertex of that rect gets -outerRad as its radius
2465// this makes the computation of the distance to the outer edge be negative
2466// negative values are caught and then handled differently in the GP's onEmitCode
2467// each vertex is also given the normalized x & y distance from the interior rect's edge
2468// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2469
Brian Salomon05441c42017-05-15 16:45:49 -04002470class CircularRRectOp : public GrMeshDrawOp {
2471private:
2472 using Helper = GrSimpleMeshDrawOpHelper;
2473
joshualitt76e7fb62015-02-11 08:52:27 -08002474public:
Brian Salomon25a88092016-12-01 09:36:50 -05002475 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002476
bsalomon4b4a7cc2016-07-08 04:42:54 -07002477 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2478 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002479 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002480 GrPaint&& paint,
2481 const SkMatrix& viewMatrix,
2482 const SkRect& devRect,
2483 float devRadius,
2484 float devStrokeWidth,
2485 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002486 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002487 devRect, devRadius,
2488 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002489 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002490 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002491 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2492 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002493 : INHERITED(ClassID())
2494 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Greg Daniel2655ede2019-04-10 00:49:28 +00002495 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002496 SkRect bounds = devRect;
2497 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2498 SkScalar innerRadius = 0.0f;
2499 SkScalar outerRadius = devRadius;
2500 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002501 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002502 if (devStrokeWidth > 0) {
2503 if (SkScalarNearlyZero(devStrokeWidth)) {
2504 halfWidth = SK_ScalarHalf;
2505 } else {
2506 halfWidth = SkScalarHalf(devStrokeWidth);
2507 }
joshualitt76e7fb62015-02-11 08:52:27 -08002508
bsalomon4b4a7cc2016-07-08 04:42:54 -07002509 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002510 // Outset stroke by 1/4 pixel
2511 devStrokeWidth += 0.25f;
2512 // If stroke is greater than width or height, this is still a fill
2513 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002514 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002515 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002516 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002517 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002518 }
2519 outerRadius += halfWidth;
2520 bounds.outset(halfWidth, halfWidth);
2521 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002522
Greg Daniel2655ede2019-04-10 00:49:28 +00002523 // The radii are outset for two reasons. First, it allows the shader to simply perform
2524 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2525 // Second, the outer radius is used to compute the verts of the bounding box that is
2526 // rendered and the outset ensures the box will cover all partially covered by the rrect
2527 // corners.
2528 outerRadius += SK_ScalarHalf;
2529 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002530
Greg Daniel5faf4742019-10-01 15:14:44 -04002531 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002532
Greg Daniel2655ede2019-04-10 00:49:28 +00002533 // Expand the rect for aa to generate correct vertices.
2534 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002535
Brian Salomon05441c42017-05-15 16:45:49 -04002536 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002537 fVertCount = rrect_type_to_vert_count(type);
2538 fIndexCount = rrect_type_to_index_count(type);
2539 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002540 }
2541
Brian Salomon289e3d82016-12-14 15:52:56 -05002542 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002543
Chris Dalton1706cbf2019-05-21 19:35:29 -06002544 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002545 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002546 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002547 } else {
2548 fHelper.visitProxies(func);
2549 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002550 }
2551
Brian Osman9a390ac2018-11-12 09:47:48 -05002552#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002553 SkString dumpInfo() const override {
2554 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002555 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002556 string.appendf(
2557 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2558 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002559 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002560 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2561 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2562 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002563 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002564 string += fHelper.dumpInfo();
2565 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002566 return string;
2567 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002568#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002569
Chris Dalton6ce447a2019-06-23 18:07:38 -06002570 GrProcessorSet::Analysis finalize(
2571 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2572 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002573 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002574 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002575 GrProcessorAnalysisCoverage::kSingleChannel, color,
2576 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002577 }
2578
2579 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2580
Brian Salomon92aee3d2016-12-21 09:20:25 -05002581private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002582 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002583 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002584 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002585 SkASSERT(smInset < bigInset);
2586
2587 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002588 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2589 color,
2590 xOffset, 0.0f,
2591 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002592
2593 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002594 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2595 color,
2596 xOffset, 0.0f,
2597 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002598
Brian Osmana1d4eb92018-12-06 16:33:10 -05002599 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2600 color,
2601 0.0f, 0.0f,
2602 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002603
Brian Osmana1d4eb92018-12-06 16:33:10 -05002604 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2605 color,
2606 0.0f, 0.0f,
2607 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002608
Brian Osmana1d4eb92018-12-06 16:33:10 -05002609 verts.write(bounds.fLeft + bigInset, bounds.fBottom - 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.fBottom - bigInset,
2615 color,
2616 0.0f, 0.0f,
2617 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002618
2619 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002620 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2621 color,
2622 xOffset, 0.0f,
2623 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002624
2625 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002626 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2627 color,
2628 xOffset, 0.0f,
2629 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002630 }
2631
Robert Phillips2669a7b2020-03-12 12:07:19 -04002632 GrProgramInfo* programInfo() override { return fProgramInfo; }
2633
Robert Phillips4133dc42020-03-11 15:55:55 -04002634 void onCreateProgramInfo(const GrCaps* caps,
2635 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002636 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002637 GrAppliedClip&& appliedClip,
2638 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002639 // Invert the view matrix as a local matrix (if any other processors require coords).
2640 SkMatrix localMatrix;
2641 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002642 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002643 }
2644
Robert Phillips4490d922020-03-03 14:50:59 -05002645 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002646 false, false, false, false,
2647 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002648
Brian Salomon8afde5f2020-04-01 16:22:00 -04002649 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002650 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002651 }
2652
Robert Phillips4490d922020-03-03 14:50:59 -05002653 void onPrepareDraws(Target* target) override {
2654 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002655 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002656 if (!fProgramInfo) {
2657 return;
2658 }
2659 }
2660
Brian Salomon12d22642019-01-29 14:38:50 -05002661 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002662 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002663
Robert Phillips4490d922020-03-03 14:50:59 -05002664 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2665 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002666 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002667 SkDebugf("Could not allocate vertices\n");
2668 return;
2669 }
2670
Brian Salomon12d22642019-01-29 14:38:50 -05002671 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002672 int firstIndex = 0;
2673 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2674 if (!indices) {
2675 SkDebugf("Could not allocate indices\n");
2676 return;
2677 }
2678
2679 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002680 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002681 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002682 SkScalar outerRadius = rrect.fOuterRadius;
2683 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002684
Brian Salomon289e3d82016-12-14 15:52:56 -05002685 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2686 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002687
Brian Salomon289e3d82016-12-14 15:52:56 -05002688 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002689 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002690 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002691 SkScalar innerRadius = rrect.fType != kFill_RRectType
2692 ? rrect.fInnerRadius / rrect.fOuterRadius
2693 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002694 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002695 verts.write(bounds.fLeft, yCoords[i],
2696 color,
2697 -1.0f, yOuterRadii[i],
2698 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002699
Brian Osmana1d4eb92018-12-06 16:33:10 -05002700 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2701 color,
2702 0.0f, yOuterRadii[i],
2703 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002704
Brian Osmana1d4eb92018-12-06 16:33:10 -05002705 verts.write(bounds.fRight - outerRadius, yCoords[i],
2706 color,
2707 0.0f, yOuterRadii[i],
2708 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002709
Brian Osmana1d4eb92018-12-06 16:33:10 -05002710 verts.write(bounds.fRight, yCoords[i],
2711 color,
2712 1.0f, yOuterRadii[i],
2713 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002714 }
jvanverthc3d0e422016-08-25 08:12:35 -07002715 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002716 // Effectively this is an additional stroked rrect, with its
2717 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2718 // This will give us correct AA in the center and the correct
2719 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002720 //
jvanvertha4f1af82016-08-29 07:17:47 -07002721 // Also, the outer offset is a constant vector pointing to the right, which
2722 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002723 if (kOverstroke_RRectType == rrect.fType) {
2724 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002725
Brian Salomon05441c42017-05-15 16:45:49 -04002726 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002727 // this is the normalized distance from the outer rectangle of this
2728 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002729 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002730
Brian Osmana1d4eb92018-12-06 16:33:10 -05002731 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002732 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002733 }
jvanverth6a397612016-08-26 08:15:33 -07002734
Brian Salomon05441c42017-05-15 16:45:49 -04002735 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2736 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002737 for (int i = 0; i < primIndexCount; ++i) {
2738 *indices++ = primIndices[i] + currStartVertex;
2739 }
2740
Brian Salomon05441c42017-05-15 16:45:49 -04002741 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002742 }
2743
Robert Phillips4490d922020-03-03 14:50:59 -05002744 fMesh = target->allocMesh();
2745 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002746 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002747 }
2748
2749 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002750 if (!fProgramInfo || !fMesh) {
2751 return;
2752 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002753
Chris Dalton765ed362020-03-16 17:34:44 -06002754 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2755 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2756 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002757 }
2758
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002759 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2760 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002761 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002762
2763 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002764 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002765 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002766 }
2767
Brian Salomon05441c42017-05-15 16:45:49 -04002768 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002769 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002770 }
2771
Brian Salomon05441c42017-05-15 16:45:49 -04002772 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002773 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2774 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002775 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002776 }
2777
Brian Salomon05441c42017-05-15 16:45:49 -04002778 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002779 fVertCount += that->fVertCount;
2780 fIndexCount += that->fIndexCount;
2781 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002782 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002783 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002784 }
2785
Brian Salomon05441c42017-05-15 16:45:49 -04002786 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002787 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002788 SkScalar fInnerRadius;
2789 SkScalar fOuterRadius;
2790 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002791 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002792 };
2793
Brian Salomon289e3d82016-12-14 15:52:56 -05002794 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002795 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002796 int fVertCount;
2797 int fIndexCount;
2798 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002799 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002800 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002801
Chris Daltoneb694b72020-03-16 09:25:50 -06002802 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002803 GrProgramInfo* fProgramInfo = nullptr;
2804
Brian Salomon05441c42017-05-15 16:45:49 -04002805 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002806};
2807
jvanverth84839f62016-08-29 10:16:40 -07002808static const int kNumRRectsInIndexBuffer = 256;
2809
2810GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2811GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002812static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2813 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002814 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2815 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2816 switch (type) {
2817 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002818 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002819 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2820 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002821 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002822 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002823 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2824 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002825 default:
2826 SkASSERT(false);
2827 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002828 }
jvanverth84839f62016-08-29 10:16:40 -07002829}
2830
Brian Salomon05441c42017-05-15 16:45:49 -04002831class EllipticalRRectOp : public GrMeshDrawOp {
2832private:
2833 using Helper = GrSimpleMeshDrawOpHelper;
2834
joshualitt76e7fb62015-02-11 08:52:27 -08002835public:
Brian Salomon25a88092016-12-01 09:36:50 -05002836 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002837
bsalomon4b4a7cc2016-07-08 04:42:54 -07002838 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2839 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002840 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002841 GrPaint&& paint,
2842 const SkMatrix& viewMatrix,
2843 const SkRect& devRect,
2844 float devXRadius,
2845 float devYRadius,
2846 SkVector devStrokeWidths,
2847 bool strokeOnly) {
Brian Osman9fb7fa52019-07-01 16:48:39 -04002848 SkASSERT(devXRadius >= 0.5);
2849 SkASSERT(devYRadius >= 0.5);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002850 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2851 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002852 if (devStrokeWidths.fX > 0) {
2853 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2854 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2855 } else {
2856 devStrokeWidths.scale(SK_ScalarHalf);
2857 }
joshualitt76e7fb62015-02-11 08:52:27 -08002858
bsalomon4b4a7cc2016-07-08 04:42:54 -07002859 // we only handle thick strokes for near-circular ellipses
2860 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002861 (SK_ScalarHalf * devXRadius > devYRadius ||
2862 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002863 return nullptr;
2864 }
2865
2866 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002867 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2868 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002869 return nullptr;
2870 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002871 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2872 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002873 return nullptr;
2874 }
Brian Salomon05441c42017-05-15 16:45:49 -04002875 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002876 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002877 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002878 devXRadius, devYRadius, devStrokeWidths,
2879 strokeOnly);
2880 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002881
Greg Daniel2655ede2019-04-10 00:49:28 +00002882 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002883 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2884 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002885 : INHERITED(ClassID())
2886 , fHelper(helperArgs, GrAAType::kCoverage)
2887 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002888 SkScalar innerXRadius = 0.0f;
2889 SkScalar innerYRadius = 0.0f;
2890 SkRect bounds = devRect;
2891 bool stroked = false;
2892 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 // this is legit only if scale & translation (which should be the case at the moment)
2894 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002895 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2896 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002897 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2898 }
2899
Brian Salomon05441c42017-05-15 16:45:49 -04002900 devXRadius += devStrokeHalfWidths.fX;
2901 devYRadius += devStrokeHalfWidths.fY;
2902 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903 }
2904
Brian Salomon05441c42017-05-15 16:45:49 -04002905 fStroked = stroked;
2906 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002907 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002908 // Expand the rect for aa in order to generate the correct vertices.
2909 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002910 fRRects.emplace_back(
2911 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002912 }
2913
Brian Salomon289e3d82016-12-14 15:52:56 -05002914 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002915
Chris Dalton1706cbf2019-05-21 19:35:29 -06002916 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002917 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002918 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002919 } else {
2920 fHelper.visitProxies(func);
2921 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002922 }
2923
Brian Osman9a390ac2018-11-12 09:47:48 -05002924#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002925 SkString dumpInfo() const override {
2926 SkString string;
2927 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002928 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002929 string.appendf(
2930 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2931 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002932 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002933 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2934 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002935 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002936 string += fHelper.dumpInfo();
2937 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002938 return string;
2939 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002940#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002941
Chris Dalton6ce447a2019-06-23 18:07:38 -06002942 GrProcessorSet::Analysis finalize(
2943 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2944 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002945 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002946 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002947 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002948 GrProcessorAnalysisCoverage::kSingleChannel, color,
2949 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002950 }
2951
2952 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2953
bsalomone46f9fe2015-08-18 06:05:14 -07002954private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002955 GrProgramInfo* programInfo() override { return fProgramInfo; }
2956
Robert Phillips4133dc42020-03-11 15:55:55 -04002957 void onCreateProgramInfo(const GrCaps* caps,
2958 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002959 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002960 GrAppliedClip&& appliedClip,
2961 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002962 SkMatrix localMatrix;
2963 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002964 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002965 }
2966
Robert Phillips4490d922020-03-03 14:50:59 -05002967 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2968 fUseScale, localMatrix);
2969
Brian Salomon8afde5f2020-04-01 16:22:00 -04002970 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002972 }
2973
Robert Phillips4490d922020-03-03 14:50:59 -05002974 void onPrepareDraws(Target* target) override {
2975 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002976 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002977 if (!fProgramInfo) {
2978 return;
2979 }
2980 }
joshualitt76e7fb62015-02-11 08:52:27 -08002981
bsalomonb5238a72015-05-05 07:49:49 -07002982 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002983 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002984 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2985 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002986
Brian Salomon12d22642019-01-29 14:38:50 -05002987 if (!indexBuffer) {
2988 SkDebugf("Could not allocate indices\n");
2989 return;
2990 }
Robert Phillips4490d922020-03-03 14:50:59 -05002991 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2992 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002993 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002994 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002995 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002996 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002997 SkDebugf("Could not allocate vertices\n");
2998 return;
2999 }
3000
Brian Salomon05441c42017-05-15 16:45:49 -04003001 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003002 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003003 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003004 float reciprocalRadii[4] = {
3005 SkScalarInvert(rrect.fXRadius),
3006 SkScalarInvert(rrect.fYRadius),
3007 SkScalarInvert(rrect.fInnerXRadius),
3008 SkScalarInvert(rrect.fInnerYRadius)
3009 };
joshualitt76e7fb62015-02-11 08:52:27 -08003010
3011 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003012 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3013 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003014
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003015 SkScalar xMaxOffset = xOuterRadius;
3016 SkScalar yMaxOffset = yOuterRadius;
3017 if (!fStroked) {
3018 // For filled rrects we map a unit circle in the vertex attributes rather than
3019 // computing an ellipse and modifying that distance, so we normalize to 1.
3020 xMaxOffset /= rrect.fXRadius;
3021 yMaxOffset /= rrect.fYRadius;
3022 }
3023
Brian Salomon05441c42017-05-15 16:45:49 -04003024 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003025
Brian Salomon289e3d82016-12-14 15:52:56 -05003026 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3027 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003028 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003029 SK_ScalarNearlyZero, // we're using inversesqrt() in
3030 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003031 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003032
Brian Osman788b9162020-02-07 10:36:46 -05003033 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003034 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003035 verts.write(bounds.fLeft, yCoords[i],
3036 color,
3037 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003038 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003039 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003040
Brian Osmana1d4eb92018-12-06 16:33:10 -05003041 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3042 color,
3043 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003044 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003045 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003046
Brian Osmana1d4eb92018-12-06 16:33:10 -05003047 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3048 color,
3049 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003050 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003051 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003052
Brian Osmana1d4eb92018-12-06 16:33:10 -05003053 verts.write(bounds.fRight, yCoords[i],
3054 color,
3055 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003056 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003057 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003058 }
3059 }
Robert Phillips4490d922020-03-03 14:50:59 -05003060 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003061 }
3062
3063 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003064 if (!fProgramInfo || !fMesh) {
3065 return;
3066 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003067
Chris Dalton765ed362020-03-16 17:34:44 -06003068 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3069 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3070 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003071 }
3072
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003073 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3074 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003075 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003076
Brian Salomon05441c42017-05-15 16:45:49 -04003077 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003078 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003079 }
3080
bsalomoncdaa97b2016-03-08 08:30:14 -08003081 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003082 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003083 }
3084
Brian Salomon05441c42017-05-15 16:45:49 -04003085 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003086 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3087 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003088 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003089 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003090
Brian Salomon05441c42017-05-15 16:45:49 -04003091 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003092 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003093 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003094 }
3095
Brian Salomon05441c42017-05-15 16:45:49 -04003096 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003097 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003098 SkScalar fXRadius;
3099 SkScalar fYRadius;
3100 SkScalar fInnerXRadius;
3101 SkScalar fInnerYRadius;
3102 SkRect fDevBounds;
3103 };
3104
Brian Salomon289e3d82016-12-14 15:52:56 -05003105 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003106 Helper fHelper;
3107 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003108 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003109 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003110 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003111
Chris Daltoneb694b72020-03-16 09:25:50 -06003112 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003113 GrProgramInfo* fProgramInfo = nullptr;
3114
Brian Salomon05441c42017-05-15 16:45:49 -04003115 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003116};
3117
Jim Van Verth64b85892019-06-17 12:01:46 -04003118std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3119 GrPaint&& paint,
3120 const SkMatrix& viewMatrix,
3121 const SkRRect& rrect,
3122 const SkStrokeRec& stroke,
3123 const GrShaderCaps* shaderCaps) {
3124 SkASSERT(viewMatrix.rectStaysRect());
3125 SkASSERT(viewMatrix.isSimilarity());
3126 SkASSERT(rrect.isSimple());
3127 SkASSERT(!rrect.isOval());
3128 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3129
3130 // RRect ops only handle simple, but not too simple, rrects.
3131 // Do any matrix crunching before we reset the draw state for device coords.
3132 const SkRect& rrectBounds = rrect.getBounds();
3133 SkRect bounds;
3134 viewMatrix.mapRect(&bounds, rrectBounds);
3135
3136 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3137 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3138 viewMatrix[SkMatrix::kMSkewY]));
3139
3140 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3141 SkScalar scaledStroke = -1;
3142 SkScalar strokeWidth = stroke.getWidth();
3143 SkStrokeRec::Style style = stroke.getStyle();
3144
3145 bool isStrokeOnly =
3146 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3147 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3148
3149 if (hasStroke) {
3150 if (SkStrokeRec::kHairline_Style == style) {
3151 scaledStroke = SK_Scalar1;
3152 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003153 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3154 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003155 }
3156 }
3157
3158 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3159 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3160 // patch will have fractional coverage. This only matters when the interior is actually filled.
3161 // We could consider falling back to rect rendering here, since a tiny radius is
3162 // indistinguishable from a square corner.
3163 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3164 return nullptr;
3165 }
3166
3167 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3168 scaledStroke, isStrokeOnly);
3169}
3170
Robert Phillipsb97da532019-02-12 15:24:12 -05003171static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003172 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003173 const SkMatrix& viewMatrix,
3174 const SkRRect& rrect,
3175 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003176 SkASSERT(viewMatrix.rectStaysRect());
3177 SkASSERT(rrect.isSimple());
3178 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003179
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003180 // RRect ops only handle simple, but not too simple, rrects.
3181 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003182 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003183 SkRect bounds;
3184 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003185
Mike Reed242135a2018-02-22 13:41:39 -05003186 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003187 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3188 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3189 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3190 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003191
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003192 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193
bsalomon4b4a7cc2016-07-08 04:42:54 -07003194 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3195 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003196 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197
Brian Salomon289e3d82016-12-14 15:52:56 -05003198 bool isStrokeOnly =
3199 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003200 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3201
3202 if (hasStroke) {
3203 if (SkStrokeRec::kHairline_Style == style) {
3204 scaledStroke.set(1, 1);
3205 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003206 scaledStroke.fX = SkScalarAbs(
3207 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3208 scaledStroke.fY = SkScalarAbs(
3209 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003210 }
3211
Jim Van Verth64b85892019-06-17 12:01:46 -04003212 // if half of strokewidth is greater than radius, we don't handle that right now
3213 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3214 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003215 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003216 }
3217 }
3218
Brian Salomon8a97f562019-04-18 14:07:27 -04003219 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003220 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003221 std::swap(xRadius, yRadius);
3222 std::swap(scaledStroke.fX, scaledStroke.fY);
3223 }
3224
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003225 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3226 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3227 // patch will have fractional coverage. This only matters when the interior is actually filled.
3228 // We could consider falling back to rect rendering here, since a tiny radius is
3229 // indistinguishable from a square corner.
3230 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003231 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003232 }
3233
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003234 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003235 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3236 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003237}
3238
Robert Phillipsb97da532019-02-12 15:24:12 -05003239std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003240 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003241 const SkMatrix& viewMatrix,
3242 const SkRRect& rrect,
3243 const SkStrokeRec& stroke,
3244 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003245 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003246 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003247 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003248 }
3249
3250 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003251 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003252 }
3253
Greg Daniel2655ede2019-04-10 00:49:28 +00003254 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003255}
joshualitt3e708c52015-04-30 13:49:27 -07003256
bsalomon4b4a7cc2016-07-08 04:42:54 -07003257///////////////////////////////////////////////////////////////////////////////
3258
Jim Van Verth64b85892019-06-17 12:01:46 -04003259std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3260 GrPaint&& paint,
3261 const SkMatrix& viewMatrix,
3262 const SkRect& oval,
3263 const GrStyle& style,
3264 const GrShaderCaps* shaderCaps) {
3265 SkScalar width = oval.width();
3266 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3267 circle_stays_circle(viewMatrix));
3268
3269 auto r = width / 2.f;
3270 SkPoint center = { oval.centerX(), oval.centerY() };
3271 if (style.hasNonDashPathEffect()) {
3272 return nullptr;
3273 } else if (style.isDashed()) {
3274 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3275 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3276 return nullptr;
3277 }
3278 auto onInterval = style.dashIntervals()[0];
3279 auto offInterval = style.dashIntervals()[1];
3280 if (offInterval == 0) {
3281 GrStyle strokeStyle(style.strokeRec(), nullptr);
3282 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3283 strokeStyle, shaderCaps);
3284 } else if (onInterval == 0) {
3285 // There is nothing to draw but we have no way to indicate that here.
3286 return nullptr;
3287 }
3288 auto angularOnInterval = onInterval / r;
3289 auto angularOffInterval = offInterval / r;
3290 auto phaseAngle = style.dashPhase() / r;
3291 // Currently this function doesn't accept ovals with different start angles, though
3292 // it could.
3293 static const SkScalar kStartAngle = 0.f;
3294 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3295 style.strokeRec().getWidth(), kStartAngle,
3296 angularOnInterval, angularOffInterval, phaseAngle);
3297 }
3298 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3299}
3300
Robert Phillipsb97da532019-02-12 15:24:12 -05003301std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003302 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003303 const SkMatrix& viewMatrix,
3304 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003305 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003306 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003307 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003308 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003309 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3310 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003311 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003312 }
3313
3314 if (style.pathEffect()) {
3315 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003316 }
3317
Stan Ilieveb868aa2017-02-21 11:06:16 -05003318 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003319 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003320 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003321 }
3322
Stan Ilieveb868aa2017-02-21 11:06:16 -05003323 // Otherwise, if we have shader derivative support, render as device-independent
3324 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003325 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3326 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3327 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3328 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3329 // Check for near-degenerate matrix
3330 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003331 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003332 style.strokeRec());
3333 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003334 }
3335
bsalomon4b4a7cc2016-07-08 04:42:54 -07003336 return nullptr;
3337}
3338
3339///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003340
Robert Phillipsb97da532019-02-12 15:24:12 -05003341std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003342 GrPaint&& paint,
3343 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003344 const SkRect& oval, SkScalar startAngle,
3345 SkScalar sweepAngle, bool useCenter,
3346 const GrStyle& style,
3347 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003348 SkASSERT(!oval.isEmpty());
3349 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003350 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003351 if (SkScalarAbs(sweepAngle) >= 360.f) {
3352 return nullptr;
3353 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003354 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3355 return nullptr;
3356 }
3357 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003358 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3359 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003360 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003361 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003362}
3363
3364///////////////////////////////////////////////////////////////////////////////
3365
Hal Canary6f6961e2017-01-31 13:50:44 -05003366#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003367
Brian Salomon05441c42017-05-15 16:45:49 -04003368GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003369 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003370 SkScalar rotate = random->nextSScalar1() * 360.f;
3371 SkScalar translateX = random->nextSScalar1() * 1000.f;
3372 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003373 SkScalar scale;
3374 do {
3375 scale = random->nextSScalar1() * 100.f;
3376 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003377 SkMatrix viewMatrix;
3378 viewMatrix.setRotate(rotate);
3379 viewMatrix.postTranslate(translateX, translateY);
3380 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003381 SkRect circle = GrTest::TestSquare(random);
3382 SkPoint center = {circle.centerX(), circle.centerY()};
3383 SkScalar radius = circle.width() / 2.f;
3384 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003385 CircleOp::ArcParams arcParamsTmp;
3386 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003387 if (random->nextBool()) {
3388 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003389 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3390 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003391 arcParams = &arcParamsTmp;
3392 }
Greg Daniel2655ede2019-04-10 00:49:28 +00003393 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003394 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003395 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003396 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003397 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003398 }
Mike Klein16885072018-12-11 09:54:31 -05003399 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003400 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003401}
3402
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003403GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3404 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);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003411 SkMatrix viewMatrix;
3412 viewMatrix.setRotate(rotate);
3413 viewMatrix.postTranslate(translateX, translateY);
3414 viewMatrix.postScale(scale, scale);
3415 SkRect circle = GrTest::TestSquare(random);
3416 SkPoint center = {circle.centerX(), circle.centerY()};
3417 SkScalar radius = circle.width() / 2.f;
3418 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3419 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3420 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3421 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3422 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003423 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3424 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003425 startAngle, onAngle, offAngle, phase);
3426}
3427
Brian Salomon05441c42017-05-15 16:45:49 -04003428GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003429 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003430 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003431 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003432 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003433}
3434
Brian Salomon05441c42017-05-15 16:45:49 -04003435GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003436 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003437 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003438 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003439 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003440}
3441
Jim Van Verth64b85892019-06-17 12:01:46 -04003442GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3443 do {
3444 SkScalar rotate = random->nextSScalar1() * 360.f;
3445 SkScalar translateX = random->nextSScalar1() * 1000.f;
3446 SkScalar translateY = random->nextSScalar1() * 1000.f;
3447 SkScalar scale;
3448 do {
3449 scale = random->nextSScalar1() * 100.f;
3450 } while (scale == 0);
3451 SkMatrix viewMatrix;
3452 viewMatrix.setRotate(rotate);
3453 viewMatrix.postTranslate(translateX, translateY);
3454 viewMatrix.postScale(scale, scale);
3455 SkRect rect = GrTest::TestRect(random);
3456 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3457 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3458 if (rrect.isOval()) {
3459 continue;
3460 }
3461 std::unique_ptr<GrDrawOp> op =
3462 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3463 GrTest::TestStrokeRec(random), nullptr);
3464 if (op) {
3465 return op;
3466 }
3467 assert_alive(paint);
3468 } while (true);
3469}
3470
Brian Salomon05441c42017-05-15 16:45:49 -04003471GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003472 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003473 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003474 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003475 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003476}
3477
3478#endif