blob: d85755781bf2e9ecc6fc2b8e8407c36cba52bb64 [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());
joshualittabb52a12015-01-13 15:02:10 -0800158
159 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800160 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800161 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800162 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500163 cgp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700164 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700165 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800166
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400167 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500168 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000169 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800170 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500171 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500172 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000173 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800174 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000175 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000176
Brian Salomon92be2f72018-06-19 14:33:47 -0400177 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000179 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
180 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400181 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500182 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000183 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
184 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700185 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400186 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500187 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000188 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
189 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 }
191 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400192 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400193 // We compute coverage of the round caps as circles at the butt caps produced
194 // by the clip planes. The inverse of the clip planes is applied so that there
195 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400196 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500197 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
198 " roundCapCenters.xy)));"
199 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
200 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400201 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400202 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
203 capRadius.fsIn(), capRadius.fsIn());
204 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700205 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000206 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207 }
208
robertphillips46d36f02015-01-18 08:14:14 -0800209 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500210 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700211 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800212 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700215 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon92be2f72018-06-19 14:33:47 -0400216 key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
217 key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
218 key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
219 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700220 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 }
222
bsalomona624bf32016-09-20 09:12:47 -0700223 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500224 const CoordTransformRange& transformRange) override {
bsalomone4f24612016-08-17 10:30:17 -0700225 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
Brian Salomonc241b582019-11-27 08:57:17 -0500226 pdman, transformRange);
joshualitte3ababe2015-05-15 07:56:07 -0700227 }
228
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229 private:
egdaniele659a582015-11-13 09:55:43 -0800230 typedef GrGLSLGeometryProcessor INHERITED;
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());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400392
393 // emit transforms
394 this->emitTransforms(vertBuilder,
395 varyingHandler,
396 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500397 bcscgp.fInPosition.asShaderVar(),
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400398 bcscgp.fLocalMatrix,
399 args.fFPCoordTransformHandler);
400 GrShaderVar fnArgs[] = {
401 GrShaderVar("angleToEdge", kFloat_GrSLType),
402 GrShaderVar("diameter", kFloat_GrSLType),
403 };
404 SkString fnName;
405 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
406 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
407 float linearDist;
408 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
409 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400410 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400411 )",
412 &fnName);
413 fragBuilder->codeAppend(R"(
414 float d = length(circleEdge.xy) * circleEdge.z;
415
416 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500417 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400418 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500419 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400420 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 edgeAlpha *= innerAlpha;
422
Ethan Nicholase1f55022019-02-05 17:17:40 -0500423 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400424 angleFromStart = mod(angleFromStart, 6.28318530718);
425 float x = mod(angleFromStart, dashParams.y);
426 // Convert the radial distance from center to pixel into a diameter.
427 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500428 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
429 half(dashParams.w));
430 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
431 half(dashParams.y) + half(dashParams.x) -
432 half(dashParams.w));
433 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
434 half(-dashParams.y) + half(dashParams.x) -
435 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 half dashAlpha = 0;
437 )");
438 fragBuilder->codeAppendf(R"(
439 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500440 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400441 currDash.y = min(currDash.y, lastIntervalLength);
442 if (nextDash.x >= lastIntervalLength) {
443 // The next dash is outside the 0..2pi range, throw it away
444 nextDash.xy = half2(1000);
445 } else {
446 // Clip the end of the next dash to the end of the circle
447 nextDash.y = min(nextDash.y, lastIntervalLength);
448 }
449 }
450 )", fnName.c_str(), fnName.c_str());
451 fragBuilder->codeAppendf(R"(
452 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500453 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400454 currDash.x = max(currDash.x, 0);
455 if (prevDash.y <= 0) {
456 // The previous dash is outside the 0..2pi range, throw it away
457 prevDash.xy = half2(1000);
458 } else {
459 // Clip the start previous dash to the start of the circle
460 prevDash.x = max(prevDash.x, 0);
461 }
462 }
463 )", fnName.c_str(), fnName.c_str());
464 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500465 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
466 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
467 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400468 dashAlpha = min(dashAlpha, 1);
469 edgeAlpha *= dashAlpha;
470 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
471 fnName.c_str());
472 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
473 }
474
475 static void GenKey(const GrGeometryProcessor& gp,
476 const GrShaderCaps&,
477 GrProcessorKeyBuilder* b) {
478 const ButtCapDashedCircleGeometryProcessor& bcscgp =
479 gp.cast<ButtCapDashedCircleGeometryProcessor>();
480 b->add32(bcscgp.fLocalMatrix.hasPerspective());
481 }
482
483 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500484 const CoordTransformRange& transformRange) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400485 this->setTransformDataHelper(
486 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
Brian Salomonc241b582019-11-27 08:57:17 -0500487 transformRange);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400488 }
489
490 private:
491 typedef GrGLSLGeometryProcessor INHERITED;
492 };
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());
joshualittabb52a12015-01-13 15:02:10 -0800591
592 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800593 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800594 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800595 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500596 egp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700597 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700598 args.fFPCoordTransformHandler);
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400599 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
600 // to compute both the edges because we need two separate test equations for
601 // the single offset.
602 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
603 // the distance by the gradient, non-uniformly scaled by the inverse of the
604 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800605
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400606 // On medium precision devices, we scale the denominator of the distance equation
607 // before taking the inverse square root to minimize the chance that we're dividing
608 // by zero, then we scale the result back.
609
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000610 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400611 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400612 if (egp.fStroke) {
613 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
614 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400615 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
616 if (egp.fUseScale) {
617 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
618 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
619 } else {
620 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
621 }
622 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700623
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000624 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400625 if (args.fShaderCaps->floatIs32Bits()) {
626 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
627 } else {
628 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
629 }
630 if (egp.fUseScale) {
631 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
632 ellipseOffsets.fsIn());
633 } else {
634 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
635 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000636 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000637
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000638 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800639 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400640 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800641 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400642 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400643 if (egp.fUseScale) {
644 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
645 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
646 } else {
647 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
648 }
649 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
650 if (!args.fShaderCaps->floatIs32Bits()) {
651 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
652 }
653 if (egp.fUseScale) {
654 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
655 ellipseOffsets.fsIn());
656 } else {
657 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
658 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000659 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660 }
661
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400662 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 }
664
robertphillips46d36f02015-01-18 08:14:14 -0800665 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500666 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700667 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800668 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
669 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700670 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700671 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000672 }
673
bsalomona624bf32016-09-20 09:12:47 -0700674 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
Brian Salomonc241b582019-11-27 08:57:17 -0500675 const CoordTransformRange& transformRange) override {
bsalomona624bf32016-09-20 09:12:47 -0700676 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Brian Salomonc241b582019-11-27 08:57:17 -0500677 this->setTransformDataHelper(egp.fLocalMatrix, pdman, transformRange);
joshualitte3ababe2015-05-15 07:56:07 -0700678 }
679
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 private:
egdaniele659a582015-11-13 09:55:43 -0800681 typedef GrGLSLGeometryProcessor INHERITED;
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);
joshualittabb52a12015-01-13 15:02:10 -0800794
795 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800796 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800797 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800798 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500799 diegp.fInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700800 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800801
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000802 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
804 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
805 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
806 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500807 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400808 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
809 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500810 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400811 if (diegp.fUseScale) {
812 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
813 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000814
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400815 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000816 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400817 if (args.fShaderCaps->floatIs32Bits()) {
818 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
819 } else {
820 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
821 }
822 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
823 if (diegp.fUseScale) {
824 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
825 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800826 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000827 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000828 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
829 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000830 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000831 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000832 }
833
834 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800835 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800836 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
837 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400838 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
839 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500840 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400841 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
842 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500843 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400844 if (diegp.fUseScale) {
845 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
846 }
847 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
848 if (!args.fShaderCaps->floatIs32Bits()) {
849 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
850 }
851 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
852 if (diegp.fUseScale) {
853 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
854 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000855 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000856 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000857
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400858 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000859 }
860
robertphillips46d36f02015-01-18 08:14:14 -0800861 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500862 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700863 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800864 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Greg Daniel2655ede2019-04-10 00:49:28 +0000865 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
866 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700867 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000868 }
869
bsalomona624bf32016-09-20 09:12:47 -0700870 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
Brian Salomonc241b582019-11-27 08:57:17 -0500871 const CoordTransformRange& transformRange) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800872 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700873
Mike Reed2c383152019-12-18 16:47:47 -0500874 if (!diegp.fViewMatrix.isIdentity() &&
875 !SkMatrixPriv::CheapEqual(fViewMatrix, diegp.fViewMatrix))
876 {
bsalomon31df31c2016-08-17 09:00:24 -0700877 fViewMatrix = diegp.fViewMatrix;
Brian Osman0b1e5fc2020-03-18 11:27:10 -0400878 pdman.setSkMatrix(fViewMatrixUniform, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700879 }
Brian Salomonc241b582019-11-27 08:57:17 -0500880 this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000881 }
882
883 private:
joshualitt5559ca22015-05-21 15:50:36 -0700884 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700885 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800886
egdaniele659a582015-11-13 09:55:43 -0800887 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888 };
889
Brian Salomon92be2f72018-06-19 14:33:47 -0400890
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500891 Attribute fInPosition;
892 Attribute fInColor;
893 Attribute fInEllipseOffsets0;
894 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400895
Brian Salomon289e3d82016-12-14 15:52:56 -0500896 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400897 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500898 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000899
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400900 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000901
joshualitt249af152014-09-15 11:41:13 -0700902 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000903};
904
bsalomoncdaa97b2016-03-08 08:30:14 -0800905GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000906
Hal Canary6f6961e2017-01-31 13:50:44 -0500907#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500908GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
909 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
910 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
911 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000912}
Hal Canary6f6961e2017-01-31 13:50:44 -0500913#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000914
915///////////////////////////////////////////////////////////////////////////////
916
jvanverth6ca48822016-10-07 06:57:32 -0700917// We have two possible cases for geometry for a circle:
918
919// In the case of a normal fill, we draw geometry for the circle as an octagon.
920static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500921 // enter the octagon
922 // clang-format off
923 0, 1, 8, 1, 2, 8,
924 2, 3, 8, 3, 4, 8,
925 4, 5, 8, 5, 6, 8,
926 6, 7, 8, 7, 0, 8
927 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700928};
929
930// For stroked circles, we use two nested octagons.
931static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500932 // enter the octagon
933 // clang-format off
934 0, 1, 9, 0, 9, 8,
935 1, 2, 10, 1, 10, 9,
936 2, 3, 11, 2, 11, 10,
937 3, 4, 12, 3, 12, 11,
938 4, 5, 13, 4, 13, 12,
939 5, 6, 14, 5, 14, 13,
940 6, 7, 15, 6, 15, 14,
941 7, 0, 8, 7, 8, 15,
942 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700943};
944
Brian Osman9d958b52018-11-13 12:46:56 -0500945// Normalized geometry for octagons that circumscribe and lie on a circle:
946
947static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
948static constexpr SkPoint kOctagonOuter[] = {
949 SkPoint::Make(-kOctOffset, -1),
950 SkPoint::Make( kOctOffset, -1),
951 SkPoint::Make( 1, -kOctOffset),
952 SkPoint::Make( 1, kOctOffset),
953 SkPoint::Make( kOctOffset, 1),
954 SkPoint::Make(-kOctOffset, 1),
955 SkPoint::Make(-1, kOctOffset),
956 SkPoint::Make(-1, -kOctOffset),
957};
958
959// cosine and sine of pi/8
960static constexpr SkScalar kCosPi8 = 0.923579533f;
961static constexpr SkScalar kSinPi8 = 0.382683432f;
962static constexpr SkPoint kOctagonInner[] = {
963 SkPoint::Make(-kSinPi8, -kCosPi8),
964 SkPoint::Make( kSinPi8, -kCosPi8),
965 SkPoint::Make( kCosPi8, -kSinPi8),
966 SkPoint::Make( kCosPi8, kSinPi8),
967 SkPoint::Make( kSinPi8, kCosPi8),
968 SkPoint::Make(-kSinPi8, kCosPi8),
969 SkPoint::Make(-kCosPi8, kSinPi8),
970 SkPoint::Make(-kCosPi8, -kSinPi8),
971};
Brian Salomon289e3d82016-12-14 15:52:56 -0500972
jvanverth6ca48822016-10-07 06:57:32 -0700973static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
974static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
975static const int kVertsPerStrokeCircle = 16;
976static const int kVertsPerFillCircle = 9;
977
978static int circle_type_to_vert_count(bool stroked) {
979 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
980}
981
982static int circle_type_to_index_count(bool stroked) {
983 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
984}
985
986static const uint16_t* circle_type_to_indices(bool stroked) {
987 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
988}
989
990///////////////////////////////////////////////////////////////////////////////
991
Brian Salomon05441c42017-05-15 16:45:49 -0400992class CircleOp final : public GrMeshDrawOp {
993private:
994 using Helper = GrSimpleMeshDrawOpHelper;
995
joshualitt76e7fb62015-02-11 08:52:27 -0800996public:
Brian Salomon25a88092016-12-01 09:36:50 -0500997 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700998
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 /** Optional extra params to render a partial arc rather than a full circle. */
1000 struct ArcParams {
1001 SkScalar fStartAngleRadians;
1002 SkScalar fSweepAngleRadians;
1003 bool fUseCenter;
1004 };
Brian Salomon05441c42017-05-15 16:45:49 -04001005
Robert Phillipsb97da532019-02-12 15:24:12 -05001006 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001007 GrPaint&& paint,
1008 const SkMatrix& viewMatrix,
1009 SkPoint center,
1010 SkScalar radius,
1011 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -04001012 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001013 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001014 if (style.hasPathEffect()) {
1015 return nullptr;
1016 }
Brian Salomon05441c42017-05-15 16:45:49 -04001017 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001018 SkStrokeRec::Style recStyle = stroke.getStyle();
1019 if (arcParams) {
1020 // Arc support depends on the style.
1021 switch (recStyle) {
1022 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001023 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001024 return nullptr;
1025 case SkStrokeRec::kFill_Style:
1026 // This supports all fills.
1027 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001028 case SkStrokeRec::kStroke_Style:
1029 // Strokes that don't use the center point are supported with butt and round
1030 // caps.
1031 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1032 return nullptr;
1033 }
1034 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001035 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001036 // Hairline only supports butt cap. Round caps could be emulated by slightly
1037 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001038 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1039 return nullptr;
1040 }
1041 break;
1042 }
1043 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001044 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1045 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001046 }
1047
Greg Daniel2655ede2019-04-10 00:49:28 +00001048 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001049 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1050 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001051 : GrMeshDrawOp(ClassID())
1052 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001053 const SkStrokeRec& stroke = style.strokeRec();
1054 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001055
Brian Salomon45c92202018-04-10 10:53:58 -04001056 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001057
bsalomon4b4a7cc2016-07-08 04:42:54 -07001058 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001059 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001060 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001061
Brian Salomon289e3d82016-12-14 15:52:56 -05001062 bool isStrokeOnly =
1063 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001064 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001065
jvanverth6ca48822016-10-07 06:57:32 -07001066 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001067 SkScalar outerRadius = radius;
1068 SkScalar halfWidth = 0;
1069 if (hasStroke) {
1070 if (SkScalarNearlyZero(strokeWidth)) {
1071 halfWidth = SK_ScalarHalf;
1072 } else {
1073 halfWidth = SkScalarHalf(strokeWidth);
1074 }
1075
1076 outerRadius += halfWidth;
1077 if (isStrokeOnly) {
1078 innerRadius = radius - halfWidth;
1079 }
1080 }
1081
1082 // The radii are outset for two reasons. First, it allows the shader to simply perform
1083 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1084 // Second, the outer radius is used to compute the verts of the bounding box that is
1085 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001086 outerRadius += SK_ScalarHalf;
1087 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001088 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001089 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001090
bsalomon4f3a0ca2016-08-22 13:14:26 -07001091 // This makes every point fully inside the intersection plane.
1092 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1093 // This makes every point fully outside the union plane.
1094 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001095 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001096 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1097 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001098 if (arcParams) {
1099 // The shader operates in a space where the circle is translated to be centered at the
1100 // origin. Here we compute points on the unit circle at the starting and ending angles.
1101 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001102 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1103 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001104 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001105 stopPoint.fY = SkScalarSin(endAngle);
1106 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001107
1108 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1109 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1110 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1111 startPoint.normalize();
1112 stopPoint.normalize();
1113
Brian Salomon3517aa72019-12-11 08:16:22 -05001114 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1115 // should orient the clip planes for arcs.
1116 SkASSERT(viewMatrix.isSimilarity());
1117 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1118 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1119 if (upperLeftDet < 0) {
1120 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001121 }
1122
Brian Salomon45c92202018-04-10 10:53:58 -04001123 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1124 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1125 SkPoint roundCaps[2];
1126 if (fRoundCaps) {
1127 // Compute the cap center points in the normalized space.
1128 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1129 roundCaps[0] = startPoint * midRadius;
1130 roundCaps[1] = stopPoint * midRadius;
1131 } else {
1132 roundCaps[0] = kUnusedRoundCaps[0];
1133 roundCaps[1] = kUnusedRoundCaps[1];
1134 }
1135
bsalomon4f3a0ca2016-08-22 13:14:26 -07001136 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001137 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1138 // center of the butts.
1139 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001140 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001141 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001143 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1144 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1145 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001146 if (useCenter) {
1147 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1148 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001149 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1150 if (arcParams->fSweepAngleRadians < 0) {
1151 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001152 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001153 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001154 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001155 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001156 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001157 color,
1158 innerRadius,
1159 outerRadius,
1160 {norm0.fX, norm0.fY, 0.5f},
1161 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1162 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001163 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001164 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001165 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001166 fClipPlaneIsect = false;
1167 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001168 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001169 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001170 color,
1171 innerRadius,
1172 outerRadius,
1173 {norm0.fX, norm0.fY, 0.5f},
1174 {norm1.fX, norm1.fY, 0.5f},
1175 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001176 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001177 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001178 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001179 fClipPlaneIsect = true;
1180 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001181 }
1182 } else {
1183 // We clip to a secant of the original circle.
1184 startPoint.scale(radius);
1185 stopPoint.scale(radius);
1186 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1187 norm.normalize();
1188 if (arcParams->fSweepAngleRadians > 0) {
1189 norm.negate();
1190 }
1191 SkScalar d = -norm.dot(startPoint) + 0.5f;
1192
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fCircles.emplace_back(
1194 Circle{color,
1195 innerRadius,
1196 outerRadius,
1197 {norm.fX, norm.fY, d},
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001200 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001201 devBounds,
1202 stroked});
1203 fClipPlane = true;
1204 fClipPlaneIsect = false;
1205 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001206 }
1207 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001208 fCircles.emplace_back(
1209 Circle{color,
1210 innerRadius,
1211 outerRadius,
1212 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1213 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1214 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001215 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001216 devBounds,
1217 stroked});
1218 fClipPlane = false;
1219 fClipPlaneIsect = false;
1220 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001221 }
bsalomon88cf17d2016-07-08 06:40:56 -07001222 // Use the original radius and stroke radius for the bounds so that it does not include the
1223 // AA bloat.
1224 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001225 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001226 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001227 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001228 fVertCount = circle_type_to_vert_count(stroked);
1229 fIndexCount = circle_type_to_index_count(stroked);
1230 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001231 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001232
Brian Salomon289e3d82016-12-14 15:52:56 -05001233 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001234
Chris Dalton1706cbf2019-05-21 19:35:29 -06001235 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001236 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001237 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001238 } else {
1239 fHelper.visitProxies(func);
1240 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001241 }
1242
Brian Osman9a390ac2018-11-12 09:47:48 -05001243#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001244 SkString dumpInfo() const override {
1245 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001246 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 string.appendf(
1248 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1249 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001250 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001251 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1252 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1253 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001254 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001255 string += fHelper.dumpInfo();
1256 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001257 return string;
1258 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001259#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001260
Chris Dalton6ce447a2019-06-23 18:07:38 -06001261 GrProcessorSet::Analysis finalize(
1262 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1263 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001264 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001265 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001266 GrProcessorAnalysisCoverage::kSingleChannel, color,
1267 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001268 }
1269
1270 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1271
bsalomone46f9fe2015-08-18 06:05:14 -07001272private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001273 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001274
Robert Phillips4133dc42020-03-11 15:55:55 -04001275 void onCreateProgramInfo(const GrCaps* caps,
1276 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001277 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001278 GrAppliedClip&& appliedClip,
1279 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001280 SkMatrix localMatrix;
1281 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001282 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001283 }
1284
Robert Phillips4490d922020-03-03 14:50:59 -05001285 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001286 fClipPlaneIsect, fClipPlaneUnion,
1287 fRoundCaps, fWideColor,
1288 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001289
Brian Salomon8afde5f2020-04-01 16:22:00 -04001290 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001291 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001292 }
1293
Robert Phillips4490d922020-03-03 14:50:59 -05001294 void onPrepareDraws(Target* target) override {
1295 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001296 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001297 if (!fProgramInfo) {
1298 return;
1299 }
1300 }
1301
Brian Salomon12d22642019-01-29 14:38:50 -05001302 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001303 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001304 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1305 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001306 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001307 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001308 return;
1309 }
1310
Brian Salomon12d22642019-01-29 14:38:50 -05001311 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001312 int firstIndex = 0;
1313 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1314 if (!indices) {
1315 SkDebugf("Could not allocate indices\n");
1316 return;
1317 }
1318
1319 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001320 for (const auto& circle : fCircles) {
1321 SkScalar innerRadius = circle.fInnerRadius;
1322 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001323 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001324 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001325
joshualitt76e7fb62015-02-11 08:52:27 -08001326 // The inner radius in the vertex data must be specified in normalized space.
1327 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001328 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001329
1330 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001331 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001332
Brian Osman9a24fee2018-08-03 09:48:42 -04001333 SkVector geoClipPlane = { 0, 0 };
1334 SkScalar offsetClipDist = SK_Scalar1;
1335 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1336 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1337 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1338 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1339 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1340 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1341 // the AA can extend just past the center of the circle.
1342 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1343 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1344 SkAssertResult(geoClipPlane.normalize());
1345 offsetClipDist = 0.5f / halfWidth;
1346 }
1347
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 for (int i = 0; i < 8; ++i) {
1349 // This clips the normalized offset to the half-plane we computed above. Then we
1350 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001351 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001352 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001353 vertices.write(center + offset * halfWidth,
1354 color,
1355 offset,
1356 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 }
1360 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001361 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001362 }
1363 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001365 }
1366 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001367 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001368 }
Brian Salomon45c92202018-04-10 10:53:58 -04001369 }
jvanverth6ca48822016-10-07 06:57:32 -07001370
Brian Salomon05441c42017-05-15 16:45:49 -04001371 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001372 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001373
Brian Osman7d8f82b2018-11-08 10:24:09 -05001374 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001375 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1376 color,
1377 kOctagonInner[i] * innerRadius,
1378 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001379 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001380 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001381 }
1382 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001383 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001384 }
1385 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001386 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001387 }
1388 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001389 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001390 }
Brian Salomon45c92202018-04-10 10:53:58 -04001391 }
jvanverth6ca48822016-10-07 06:57:32 -07001392 } else {
1393 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001394 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001395 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001396 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001397 }
jvanverth6ca48822016-10-07 06:57:32 -07001398 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001399 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001400 }
1401 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001402 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001403 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001404 if (fRoundCaps) {
1405 vertices.write(circle.fRoundCapCenters);
1406 }
jvanverth6ca48822016-10-07 06:57:32 -07001407 }
1408
Brian Salomon05441c42017-05-15 16:45:49 -04001409 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1410 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001411 for (int i = 0; i < primIndexCount; ++i) {
1412 *indices++ = primIndices[i] + currStartVertex;
1413 }
1414
Brian Salomon05441c42017-05-15 16:45:49 -04001415 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001416 }
jvanverth6ca48822016-10-07 06:57:32 -07001417
Robert Phillips4490d922020-03-03 14:50:59 -05001418 fMesh = target->allocMesh();
1419 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001420 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001421 }
1422
1423 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001424 if (!fProgramInfo || !fMesh) {
1425 return;
1426 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001427
Chris Dalton765ed362020-03-16 17:34:44 -06001428 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1429 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1430 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001431 }
1432
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001433 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1434 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001435 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001436
1437 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001438 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001439 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001440 }
1441
Brian Salomon05441c42017-05-15 16:45:49 -04001442 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001443 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001444 }
1445
Brian Salomon05441c42017-05-15 16:45:49 -04001446 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001447 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1448 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001449 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001450 }
1451
Brian Salomon289e3d82016-12-14 15:52:56 -05001452 // Because we've set up the ops that don't use the planes with noop values
1453 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001454 fClipPlane |= that->fClipPlane;
1455 fClipPlaneIsect |= that->fClipPlaneIsect;
1456 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001457 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001458 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001459
Brian Salomon05441c42017-05-15 16:45:49 -04001460 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001461 fVertCount += that->fVertCount;
1462 fIndexCount += that->fIndexCount;
1463 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001464 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001465 }
1466
Brian Salomon05441c42017-05-15 16:45:49 -04001467 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001468 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001469 SkScalar fInnerRadius;
1470 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001471 SkScalar fClipPlane[3];
1472 SkScalar fIsectPlane[3];
1473 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001474 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001475 SkRect fDevBounds;
1476 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001477 };
1478
Brian Salomon289e3d82016-12-14 15:52:56 -05001479 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001480 Helper fHelper;
1481 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001482 int fVertCount;
1483 int fIndexCount;
1484 bool fAllFill;
1485 bool fClipPlane;
1486 bool fClipPlaneIsect;
1487 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001488 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001489 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001490
Chris Daltoneb694b72020-03-16 09:25:50 -06001491 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001492 GrProgramInfo* fProgramInfo = nullptr;
1493
Brian Salomon05441c42017-05-15 16:45:49 -04001494 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001495};
1496
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001497class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1498private:
1499 using Helper = GrSimpleMeshDrawOpHelper;
1500
1501public:
1502 DEFINE_OP_CLASS_ID
1503
Robert Phillipsb97da532019-02-12 15:24:12 -05001504 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001505 GrPaint&& paint,
1506 const SkMatrix& viewMatrix,
1507 SkPoint center,
1508 SkScalar radius,
1509 SkScalar strokeWidth,
1510 SkScalar startAngle,
1511 SkScalar onAngle,
1512 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001513 SkScalar phaseAngle) {
1514 SkASSERT(circle_stays_circle(viewMatrix));
1515 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001516 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1517 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001518 onAngle, offAngle, phaseAngle);
1519 }
1520
Brian Osmancf860852018-10-31 14:04:39 -04001521 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001522 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1523 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1524 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001525 : GrMeshDrawOp(ClassID())
1526 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001527 SkASSERT(circle_stays_circle(viewMatrix));
1528 viewMatrix.mapPoints(&center, 1);
1529 radius = viewMatrix.mapRadius(radius);
1530 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1531
1532 // Determine the angle where the circle starts in device space and whether its orientation
1533 // has been reversed.
1534 SkVector start;
1535 bool reflection;
1536 if (!startAngle) {
1537 start = {1, 0};
1538 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001539 start.fY = SkScalarSin(startAngle);
1540 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001541 }
1542 viewMatrix.mapVectors(&start, 1);
1543 startAngle = SkScalarATan2(start.fY, start.fX);
1544 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1545 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1546
1547 auto totalAngle = onAngle + offAngle;
1548 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1549
1550 SkScalar halfWidth = 0;
1551 if (SkScalarNearlyZero(strokeWidth)) {
1552 halfWidth = SK_ScalarHalf;
1553 } else {
1554 halfWidth = SkScalarHalf(strokeWidth);
1555 }
1556
1557 SkScalar outerRadius = radius + halfWidth;
1558 SkScalar innerRadius = radius - halfWidth;
1559
1560 // The radii are outset for two reasons. First, it allows the shader to simply perform
1561 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1562 // Second, the outer radius is used to compute the verts of the bounding box that is
1563 // rendered and the outset ensures the box will cover all partially covered by the circle.
1564 outerRadius += SK_ScalarHalf;
1565 innerRadius -= SK_ScalarHalf;
1566 fViewMatrixIfUsingLocalCoords = viewMatrix;
1567
1568 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1569 center.fX + outerRadius, center.fY + outerRadius);
1570
1571 // We store whether there is a reflection as a negative total angle.
1572 if (reflection) {
1573 totalAngle = -totalAngle;
1574 }
1575 fCircles.push_back(Circle{
1576 color,
1577 outerRadius,
1578 innerRadius,
1579 onAngle,
1580 totalAngle,
1581 startAngle,
1582 phaseAngle,
1583 devBounds
1584 });
1585 // Use the original radius and stroke radius for the bounds so that it does not include the
1586 // AA bloat.
1587 radius += halfWidth;
1588 this->setBounds(
1589 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001590 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001591 fVertCount = circle_type_to_vert_count(true);
1592 fIndexCount = circle_type_to_index_count(true);
1593 }
1594
1595 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1596
Chris Dalton1706cbf2019-05-21 19:35:29 -06001597 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001598 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001599 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001600 } else {
1601 fHelper.visitProxies(func);
1602 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001603 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001604
Brian Osman9a390ac2018-11-12 09:47:48 -05001605#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001606 SkString dumpInfo() const override {
1607 SkString string;
1608 for (int i = 0; i < fCircles.count(); ++i) {
1609 string.appendf(
1610 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1611 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1612 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001613 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001614 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1615 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1616 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1617 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001618 }
1619 string += fHelper.dumpInfo();
1620 string += INHERITED::dumpInfo();
1621 return string;
1622 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001623#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001624
Chris Dalton6ce447a2019-06-23 18:07:38 -06001625 GrProcessorSet::Analysis finalize(
1626 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1627 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001628 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001629 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001630 GrProcessorAnalysisCoverage::kSingleChannel, color,
1631 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001632 }
1633
1634 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1635
1636private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001637 GrProgramInfo* programInfo() override { return fProgramInfo; }
1638
Robert Phillips4133dc42020-03-11 15:55:55 -04001639 void onCreateProgramInfo(const GrCaps* caps,
1640 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001641 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001642 GrAppliedClip&& appliedClip,
1643 const GrXferProcessor::DstProxyView& dstProxyView) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001644 SkMatrix localMatrix;
1645 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001646 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001647 }
1648
1649 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001650 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001651 fWideColor,
1652 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001653
Brian Salomon8afde5f2020-04-01 16:22:00 -04001654 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001655 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001656 }
1657
Robert Phillips4490d922020-03-03 14:50:59 -05001658 void onPrepareDraws(Target* target) override {
1659 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001660 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001661 if (!fProgramInfo) {
1662 return;
1663 }
1664 }
1665
Brian Salomon12d22642019-01-29 14:38:50 -05001666 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001668 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1669 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001670 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001671 SkDebugf("Could not allocate vertices\n");
1672 return;
1673 }
1674
Brian Salomon12d22642019-01-29 14:38:50 -05001675 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676 int firstIndex = 0;
1677 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1678 if (!indices) {
1679 SkDebugf("Could not allocate indices\n");
1680 return;
1681 }
1682
1683 int currStartVertex = 0;
1684 for (const auto& circle : fCircles) {
1685 // The inner radius in the vertex data must be specified in normalized space so that
1686 // length() can be called with smaller values to avoid precision issues with half
1687 // floats.
1688 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1689 const SkRect& bounds = circle.fDevBounds;
1690 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001691 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1692 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1693 };
1694 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001695 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001696 dashParams.totalAngle = -dashParams.totalAngle;
1697 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001698 }
1699
Brian Osmane3caf2d2018-11-21 13:48:36 -05001700 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001701
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001702 // The bounding geometry for the circle is composed of an outer bounding octagon and
1703 // an inner bounded octagon.
1704
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001705 // Compute the vertices of the outer octagon.
1706 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1707 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001708
1709 auto reflectY = [=](const SkPoint& p) {
1710 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001711 };
Brian Osman9d958b52018-11-13 12:46:56 -05001712
1713 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001714 vertices.write(center + kOctagonOuter[i] * halfWidth,
1715 color,
1716 reflectY(kOctagonOuter[i]),
1717 circle.fOuterRadius,
1718 normInnerRadius,
1719 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001720 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721
1722 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001723 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001724 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1725 color,
1726 reflectY(kOctagonInner[i]) * normInnerRadius,
1727 circle.fOuterRadius,
1728 normInnerRadius,
1729 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001730 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001731
1732 const uint16_t* primIndices = circle_type_to_indices(true);
1733 const int primIndexCount = circle_type_to_index_count(true);
1734 for (int i = 0; i < primIndexCount; ++i) {
1735 *indices++ = primIndices[i] + currStartVertex;
1736 }
1737
1738 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001739 }
1740
Robert Phillips4490d922020-03-03 14:50:59 -05001741 fMesh = target->allocMesh();
1742 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001743 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001744 }
1745
1746 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001747 if (!fProgramInfo || !fMesh) {
1748 return;
1749 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001750
Chris Dalton765ed362020-03-16 17:34:44 -06001751 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1752 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1753 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001754 }
1755
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001756 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1757 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001758 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1759
1760 // can only represent 65535 unique vertices with 16-bit indices
1761 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001762 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001763 }
1764
1765 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001766 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001767 }
1768
1769 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001770 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1771 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001772 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001773 }
1774
1775 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001776 fVertCount += that->fVertCount;
1777 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001778 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001779 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001780 }
1781
1782 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001783 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784 SkScalar fOuterRadius;
1785 SkScalar fInnerRadius;
1786 SkScalar fOnAngle;
1787 SkScalar fTotalAngle;
1788 SkScalar fStartAngle;
1789 SkScalar fPhaseAngle;
1790 SkRect fDevBounds;
1791 };
1792
1793 SkMatrix fViewMatrixIfUsingLocalCoords;
1794 Helper fHelper;
1795 SkSTArray<1, Circle, true> fCircles;
1796 int fVertCount;
1797 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001798 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001799
Chris Daltoneb694b72020-03-16 09:25:50 -06001800 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001801 GrProgramInfo* fProgramInfo = nullptr;
1802
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001803 typedef GrMeshDrawOp INHERITED;
1804};
1805
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001806///////////////////////////////////////////////////////////////////////////////
1807
Brian Salomon05441c42017-05-15 16:45:49 -04001808class EllipseOp : public GrMeshDrawOp {
1809private:
1810 using Helper = GrSimpleMeshDrawOpHelper;
1811
1812 struct DeviceSpaceParams {
1813 SkPoint fCenter;
1814 SkScalar fXRadius;
1815 SkScalar fYRadius;
1816 SkScalar fInnerXRadius;
1817 SkScalar fInnerYRadius;
1818 };
1819
joshualitt76e7fb62015-02-11 08:52:27 -08001820public:
Brian Salomon25a88092016-12-01 09:36:50 -05001821 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001822
Robert Phillipsb97da532019-02-12 15:24:12 -05001823 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001824 GrPaint&& paint,
1825 const SkMatrix& viewMatrix,
1826 const SkRect& ellipse,
1827 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001828 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001829 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001830 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1831 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001832 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1833 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001834 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1835 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1836 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1837 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001838
bsalomon4b4a7cc2016-07-08 04:42:54 -07001839 // do (potentially) anisotropic mapping of stroke
1840 SkVector scaledStroke;
1841 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001842 scaledStroke.fX = SkScalarAbs(
1843 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1844 scaledStroke.fY = SkScalarAbs(
1845 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001846
1847 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001848 bool isStrokeOnly =
1849 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1851
Brian Salomon05441c42017-05-15 16:45:49 -04001852 params.fInnerXRadius = 0;
1853 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001854 if (hasStroke) {
1855 if (SkScalarNearlyZero(scaledStroke.length())) {
1856 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1857 } else {
1858 scaledStroke.scale(SK_ScalarHalf);
1859 }
1860
1861 // we only handle thick strokes for near-circular ellipses
1862 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001863 (0.5f * params.fXRadius > params.fYRadius ||
1864 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001865 return nullptr;
1866 }
1867
1868 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001869 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1870 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1871 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1872 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873 return nullptr;
1874 }
1875
1876 // this is legit only if scale & translation (which should be the case at the moment)
1877 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001878 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1879 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001880 }
1881
Brian Salomon05441c42017-05-15 16:45:49 -04001882 params.fXRadius += scaledStroke.fX;
1883 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001884 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001885
1886 // For large ovals with low precision floats, we fall back to the path renderer.
1887 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1888 // minimum value to avoid divides by zero. With large ovals and low precision this
1889 // leads to blurring at the edge of the oval.
1890 const SkScalar kMaxOvalRadius = 16384;
1891 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1892 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1893 return nullptr;
1894 }
1895
Greg Daniel2655ede2019-04-10 00:49:28 +00001896 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001897 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001898 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001899
Brian Osmancf860852018-10-31 14:04:39 -04001900 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001901 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001902 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001903 : INHERITED(ClassID())
1904 , fHelper(helperArgs, GrAAType::kCoverage)
1905 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001906 SkStrokeRec::Style style = stroke.getStyle();
1907 bool isStrokeOnly =
1908 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001909
Brian Salomon05441c42017-05-15 16:45:49 -04001910 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1911 params.fInnerXRadius, params.fInnerYRadius,
1912 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1913 params.fCenter.fY - params.fYRadius,
1914 params.fCenter.fX + params.fXRadius,
1915 params.fCenter.fY + params.fYRadius)});
1916
Greg Daniel5faf4742019-10-01 15:14:44 -04001917 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001918
bsalomon4b4a7cc2016-07-08 04:42:54 -07001919 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001920 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001921
Brian Salomon05441c42017-05-15 16:45:49 -04001922 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1923 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001924 }
joshualitt76e7fb62015-02-11 08:52:27 -08001925
Brian Salomon289e3d82016-12-14 15:52:56 -05001926 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001927
Chris Dalton1706cbf2019-05-21 19:35:29 -06001928 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001929 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001930 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001931 } else {
1932 fHelper.visitProxies(func);
1933 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001934 }
1935
Brian Osman9a390ac2018-11-12 09:47:48 -05001936#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001937 SkString dumpInfo() const override {
1938 SkString string;
1939 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001940 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001941 string.appendf(
1942 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1943 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001944 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001945 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1946 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001947 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001948 string += fHelper.dumpInfo();
1949 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001950 return string;
1951 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001952#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001953
Chris Dalton6ce447a2019-06-23 18:07:38 -06001954 GrProcessorSet::Analysis finalize(
1955 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1956 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001957 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1958 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001959 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001960 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001961 GrProcessorAnalysisCoverage::kSingleChannel, color,
1962 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001963 }
1964
1965 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1966
bsalomone46f9fe2015-08-18 06:05:14 -07001967private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001968 GrProgramInfo* programInfo() override { return fProgramInfo; }
1969
Robert Phillips4133dc42020-03-11 15:55:55 -04001970 void onCreateProgramInfo(const GrCaps* caps,
1971 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001972 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001973 GrAppliedClip&& appliedClip,
1974 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001975 SkMatrix localMatrix;
1976 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001977 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001978 }
1979
Robert Phillips4490d922020-03-03 14:50:59 -05001980 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1981 fUseScale, localMatrix);
1982
Brian Salomon8afde5f2020-04-01 16:22:00 -04001983 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001984 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001985 }
1986
Robert Phillips4490d922020-03-03 14:50:59 -05001987 void onPrepareDraws(Target* target) override {
1988 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001989 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001990 if (!fProgramInfo) {
1991 return;
1992 }
1993 }
1994
1995 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001996 GrVertexWriter verts{helper.vertices()};
1997 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001998 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001999 return;
2000 }
2001
Brian Salomon05441c42017-05-15 16:45:49 -04002002 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002003 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002004 SkScalar xRadius = ellipse.fXRadius;
2005 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002006
2007 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05002008 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
2009 SkScalarInvert(xRadius),
2010 SkScalarInvert(yRadius),
2011 SkScalarInvert(ellipse.fInnerXRadius),
2012 SkScalarInvert(ellipse.fInnerYRadius)
2013 };
Greg Daniel2655ede2019-04-10 00:49:28 +00002014 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
2015 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07002016
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002017 if (!fStroked) {
2018 // For filled ellipses we map a unit circle in the vertex attributes rather than
2019 // computing an ellipse and modifying that distance, so we normalize to 1
2020 xMaxOffset /= xRadius;
2021 yMaxOffset /= yRadius;
2022 }
2023
joshualitt76e7fb62015-02-11 08:52:27 -08002024 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002025 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2026 color,
2027 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002028 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002029 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002030 }
Robert Phillips4490d922020-03-03 14:50:59 -05002031 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002032 }
2033
2034 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002035 if (!fProgramInfo || !fMesh) {
2036 return;
2037 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002038
Chris Dalton765ed362020-03-16 17:34:44 -06002039 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2040 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2041 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002042 }
2043
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002044 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2045 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002046 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002047
Brian Salomon05441c42017-05-15 16:45:49 -04002048 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002049 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002050 }
2051
bsalomoncdaa97b2016-03-08 08:30:14 -08002052 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002053 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002054 }
2055
Brian Salomon05441c42017-05-15 16:45:49 -04002056 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002057 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2058 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002059 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002060 }
2061
Brian Salomon05441c42017-05-15 16:45:49 -04002062 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002063 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002064 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002065 }
2066
Brian Salomon05441c42017-05-15 16:45:49 -04002067 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002068 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002069 SkScalar fXRadius;
2070 SkScalar fYRadius;
2071 SkScalar fInnerXRadius;
2072 SkScalar fInnerYRadius;
2073 SkRect fDevBounds;
2074 };
joshualitt76e7fb62015-02-11 08:52:27 -08002075
Brian Salomon289e3d82016-12-14 15:52:56 -05002076 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002077 Helper fHelper;
2078 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002079 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002080 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002081 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002082
Chris Daltoneb694b72020-03-16 09:25:50 -06002083 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002084 GrProgramInfo* fProgramInfo = nullptr;
2085
Brian Salomon05441c42017-05-15 16:45:49 -04002086 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002087};
2088
joshualitt76e7fb62015-02-11 08:52:27 -08002089/////////////////////////////////////////////////////////////////////////////////////////////////
2090
Brian Salomon05441c42017-05-15 16:45:49 -04002091class DIEllipseOp : public GrMeshDrawOp {
2092private:
2093 using Helper = GrSimpleMeshDrawOpHelper;
2094
2095 struct DeviceSpaceParams {
2096 SkPoint fCenter;
2097 SkScalar fXRadius;
2098 SkScalar fYRadius;
2099 SkScalar fInnerXRadius;
2100 SkScalar fInnerYRadius;
2101 DIEllipseStyle fStyle;
2102 };
2103
joshualitt76e7fb62015-02-11 08:52:27 -08002104public:
Brian Salomon25a88092016-12-01 09:36:50 -05002105 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002106
Robert Phillipsb97da532019-02-12 15:24:12 -05002107 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002108 GrPaint&& paint,
2109 const SkMatrix& viewMatrix,
2110 const SkRect& ellipse,
2111 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002112 DeviceSpaceParams params;
2113 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2114 params.fXRadius = SkScalarHalf(ellipse.width());
2115 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002116
bsalomon4b4a7cc2016-07-08 04:42:54 -07002117 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002118 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2119 ? DIEllipseStyle::kStroke
2120 : (SkStrokeRec::kHairline_Style == style)
2121 ? DIEllipseStyle::kHairline
2122 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002123
Brian Salomon05441c42017-05-15 16:45:49 -04002124 params.fInnerXRadius = 0;
2125 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002126 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2127 SkScalar strokeWidth = stroke.getWidth();
2128
2129 if (SkScalarNearlyZero(strokeWidth)) {
2130 strokeWidth = SK_ScalarHalf;
2131 } else {
2132 strokeWidth *= SK_ScalarHalf;
2133 }
2134
2135 // we only handle thick strokes for near-circular ellipses
2136 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002137 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2138 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002139 return nullptr;
2140 }
2141
2142 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002143 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2144 (strokeWidth * strokeWidth) * params.fXRadius) {
2145 return nullptr;
2146 }
2147 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2148 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002149 return nullptr;
2150 }
2151
2152 // set inner radius (if needed)
2153 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002154 params.fInnerXRadius = params.fXRadius - strokeWidth;
2155 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002156 }
2157
Brian Salomon05441c42017-05-15 16:45:49 -04002158 params.fXRadius += strokeWidth;
2159 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002160 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002161
2162 // For large ovals with low precision floats, we fall back to the path renderer.
2163 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2164 // minimum value to avoid divides by zero. With large ovals and low precision this
2165 // leads to blurring at the edge of the oval.
2166 const SkScalar kMaxOvalRadius = 16384;
2167 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2168 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2169 return nullptr;
2170 }
2171
Brian Salomon05441c42017-05-15 16:45:49 -04002172 if (DIEllipseStyle::kStroke == params.fStyle &&
2173 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2174 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002175 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002176 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002177 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002178
Greg Daniel2655ede2019-04-10 00:49:28 +00002179 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002180 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002181 : INHERITED(ClassID())
2182 , fHelper(helperArgs, GrAAType::kCoverage)
2183 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002184 // This expands the outer rect so that after CTM we end up with a half-pixel border
2185 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2186 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2187 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2188 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2189 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2190 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002191
Brian Salomon05441c42017-05-15 16:45:49 -04002192 fEllipses.emplace_back(
2193 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2194 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2195 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2196 params.fCenter.fY - params.fYRadius - geoDy,
2197 params.fCenter.fX + params.fXRadius + geoDx,
2198 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002199 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002200 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002201 }
2202
Brian Salomon289e3d82016-12-14 15:52:56 -05002203 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002204
Chris Dalton1706cbf2019-05-21 19:35:29 -06002205 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002206 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002207 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002208 } else {
2209 fHelper.visitProxies(func);
2210 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002211 }
2212
Brian Osman9a390ac2018-11-12 09:47:48 -05002213#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002214 SkString dumpInfo() const override {
2215 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002216 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002217 string.appendf(
2218 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2219 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2220 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002221 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002222 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2223 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002224 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002225 string += fHelper.dumpInfo();
2226 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002227 return string;
2228 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002229#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002230
Chris Dalton6ce447a2019-06-23 18:07:38 -06002231 GrProcessorSet::Analysis finalize(
2232 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2233 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002234 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2235 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002236 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002237 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002238 GrProcessorAnalysisCoverage::kSingleChannel, color,
2239 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002240 }
2241
2242 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2243
bsalomone46f9fe2015-08-18 06:05:14 -07002244private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002245 GrProgramInfo* programInfo() override { return fProgramInfo; }
2246
Robert Phillips4133dc42020-03-11 15:55:55 -04002247 void onCreateProgramInfo(const GrCaps* caps,
2248 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002249 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002250 GrAppliedClip&& appliedClip,
2251 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002252 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2253 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002254 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002255
Brian Salomon8afde5f2020-04-01 16:22:00 -04002256 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002257 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002258 }
2259
Robert Phillips4490d922020-03-03 14:50:59 -05002260 void onPrepareDraws(Target* target) override {
2261 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002262 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002263 }
2264
2265 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002266 GrVertexWriter verts{helper.vertices()};
2267 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002268 return;
2269 }
2270
Brian Salomon05441c42017-05-15 16:45:49 -04002271 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002272 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002273 SkScalar xRadius = ellipse.fXRadius;
2274 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002275
joshualitt76e7fb62015-02-11 08:52:27 -08002276 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002277 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2278 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002279
Brian Osman9d958b52018-11-13 12:46:56 -05002280 // By default, constructed so that inner offset is (0, 0) for all points
2281 SkScalar innerRatioX = -offsetDx;
2282 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002283
Brian Osman9d958b52018-11-13 12:46:56 -05002284 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002285 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002286 innerRatioX = xRadius / ellipse.fInnerXRadius;
2287 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002288 }
joshualitt76e7fb62015-02-11 08:52:27 -08002289
Brian Osman2b6e3902018-11-21 15:29:43 -05002290 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2291 color,
2292 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002293 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002294 origin_centered_tri_strip(innerRatioX + offsetDx,
2295 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002296 }
Robert Phillips4490d922020-03-03 14:50:59 -05002297 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002298 }
2299
2300 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002301 if (!fProgramInfo || !fMesh) {
2302 return;
2303 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002304
Chris Dalton765ed362020-03-16 17:34:44 -06002305 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2306 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2307 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002308 }
halcanary9d524f22016-03-29 09:03:52 -07002309
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002310 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2311 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002312 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002313 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002314 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002315 }
2316
bsalomoncdaa97b2016-03-08 08:30:14 -08002317 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002318 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002319 }
2320
joshualittd96a67b2015-05-05 14:09:05 -07002321 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002322 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002323 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002324 }
2325
Brian Salomon05441c42017-05-15 16:45:49 -04002326 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002327 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002328 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002329 }
2330
Brian Salomon05441c42017-05-15 16:45:49 -04002331 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2332 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002333
Brian Salomon05441c42017-05-15 16:45:49 -04002334 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002335 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002336 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002337 SkScalar fXRadius;
2338 SkScalar fYRadius;
2339 SkScalar fInnerXRadius;
2340 SkScalar fInnerYRadius;
2341 SkScalar fGeoDx;
2342 SkScalar fGeoDy;
2343 DIEllipseStyle fStyle;
2344 SkRect fBounds;
2345 };
2346
Brian Salomon05441c42017-05-15 16:45:49 -04002347 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002348 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002349 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002350 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002351
Chris Daltoneb694b72020-03-16 09:25:50 -06002352 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002353 GrProgramInfo* fProgramInfo = nullptr;
2354
Brian Salomon05441c42017-05-15 16:45:49 -04002355 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002356};
2357
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002358///////////////////////////////////////////////////////////////////////////////
2359
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002360// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002361//
2362// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2363// ____________
2364// |_|________|_|
2365// | | | |
2366// | | | |
2367// | | | |
2368// |_|________|_|
2369// |_|________|_|
2370//
2371// For strokes, we don't draw the center quad.
2372//
2373// For circular roundrects, in the case where the stroke width is greater than twice
2374// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002375// in the center. The shared vertices are duplicated so we can set a different outer radius
2376// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002377// ____________
2378// |_|________|_|
2379// | |\ ____ /| |
2380// | | | | | |
2381// | | |____| | |
2382// |_|/______\|_|
2383// |_|________|_|
2384//
2385// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002386//
2387// For filled rrects that need to provide a distance vector we resuse the overstroke
2388// geometry but make the inner rect degenerate (either a point or a horizontal or
2389// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002390
jvanverth84839f62016-08-29 10:16:40 -07002391static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002392 // clang-format off
2393 // overstroke quads
2394 // we place this at the beginning so that we can skip these indices when rendering normally
2395 16, 17, 19, 16, 19, 18,
2396 19, 17, 23, 19, 23, 21,
2397 21, 23, 22, 21, 22, 20,
2398 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002399
Brian Salomon289e3d82016-12-14 15:52:56 -05002400 // corners
2401 0, 1, 5, 0, 5, 4,
2402 2, 3, 7, 2, 7, 6,
2403 8, 9, 13, 8, 13, 12,
2404 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002405
Brian Salomon289e3d82016-12-14 15:52:56 -05002406 // edges
2407 1, 2, 6, 1, 6, 5,
2408 4, 5, 9, 4, 9, 8,
2409 6, 7, 11, 6, 11, 10,
2410 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002411
Brian Salomon289e3d82016-12-14 15:52:56 -05002412 // center
2413 // we place this at the end so that we can ignore these indices when not rendering as filled
2414 5, 6, 10, 5, 10, 9,
2415 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002416};
Brian Salomon289e3d82016-12-14 15:52:56 -05002417
jvanverth84839f62016-08-29 10:16:40 -07002418// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002419static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002420
jvanverth84839f62016-08-29 10:16:40 -07002421// overstroke count is arraysize minus the center indices
2422static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2423// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002424static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002425// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002426static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2427static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002428static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002429
jvanverthc3d0e422016-08-25 08:12:35 -07002430enum RRectType {
2431 kFill_RRectType,
2432 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002433 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002434};
2435
jvanverth84839f62016-08-29 10:16:40 -07002436static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002437 switch (type) {
2438 case kFill_RRectType:
2439 case kStroke_RRectType:
2440 return kVertsPerStandardRRect;
2441 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002442 return kVertsPerOverstrokeRRect;
2443 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002444 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002445}
2446
2447static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002448 switch (type) {
2449 case kFill_RRectType:
2450 return kIndicesPerFillRRect;
2451 case kStroke_RRectType:
2452 return kIndicesPerStrokeRRect;
2453 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002454 return kIndicesPerOverstrokeRRect;
2455 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002456 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002457}
2458
2459static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002460 switch (type) {
2461 case kFill_RRectType:
2462 case kStroke_RRectType:
2463 return gStandardRRectIndices;
2464 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002465 return gOverstrokeRRectIndices;
2466 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002467 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002468}
2469
joshualitt76e7fb62015-02-11 08:52:27 -08002470///////////////////////////////////////////////////////////////////////////////////////////////////
2471
Robert Phillips79839d42016-10-06 15:03:34 -04002472// For distance computations in the interior of filled rrects we:
2473//
2474// add a interior degenerate (point or line) rect
2475// each vertex of that rect gets -outerRad as its radius
2476// this makes the computation of the distance to the outer edge be negative
2477// negative values are caught and then handled differently in the GP's onEmitCode
2478// each vertex is also given the normalized x & y distance from the interior rect's edge
2479// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2480
Brian Salomon05441c42017-05-15 16:45:49 -04002481class CircularRRectOp : public GrMeshDrawOp {
2482private:
2483 using Helper = GrSimpleMeshDrawOpHelper;
2484
joshualitt76e7fb62015-02-11 08:52:27 -08002485public:
Brian Salomon25a88092016-12-01 09:36:50 -05002486 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002487
bsalomon4b4a7cc2016-07-08 04:42:54 -07002488 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2489 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002490 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002491 GrPaint&& paint,
2492 const SkMatrix& viewMatrix,
2493 const SkRect& devRect,
2494 float devRadius,
2495 float devStrokeWidth,
2496 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002497 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002498 devRect, devRadius,
2499 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002500 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002501 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002502 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2503 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002504 : INHERITED(ClassID())
2505 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Greg Daniel2655ede2019-04-10 00:49:28 +00002506 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002507 SkRect bounds = devRect;
2508 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2509 SkScalar innerRadius = 0.0f;
2510 SkScalar outerRadius = devRadius;
2511 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002512 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002513 if (devStrokeWidth > 0) {
2514 if (SkScalarNearlyZero(devStrokeWidth)) {
2515 halfWidth = SK_ScalarHalf;
2516 } else {
2517 halfWidth = SkScalarHalf(devStrokeWidth);
2518 }
joshualitt76e7fb62015-02-11 08:52:27 -08002519
bsalomon4b4a7cc2016-07-08 04:42:54 -07002520 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002521 // Outset stroke by 1/4 pixel
2522 devStrokeWidth += 0.25f;
2523 // If stroke is greater than width or height, this is still a fill
2524 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002525 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002526 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002527 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002528 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002529 }
2530 outerRadius += halfWidth;
2531 bounds.outset(halfWidth, halfWidth);
2532 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002533
Greg Daniel2655ede2019-04-10 00:49:28 +00002534 // The radii are outset for two reasons. First, it allows the shader to simply perform
2535 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2536 // Second, the outer radius is used to compute the verts of the bounding box that is
2537 // rendered and the outset ensures the box will cover all partially covered by the rrect
2538 // corners.
2539 outerRadius += SK_ScalarHalf;
2540 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002541
Greg Daniel5faf4742019-10-01 15:14:44 -04002542 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002543
Greg Daniel2655ede2019-04-10 00:49:28 +00002544 // Expand the rect for aa to generate correct vertices.
2545 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002546
Brian Salomon05441c42017-05-15 16:45:49 -04002547 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002548 fVertCount = rrect_type_to_vert_count(type);
2549 fIndexCount = rrect_type_to_index_count(type);
2550 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002551 }
2552
Brian Salomon289e3d82016-12-14 15:52:56 -05002553 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002554
Chris Dalton1706cbf2019-05-21 19:35:29 -06002555 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002556 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002557 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002558 } else {
2559 fHelper.visitProxies(func);
2560 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002561 }
2562
Brian Osman9a390ac2018-11-12 09:47:48 -05002563#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002564 SkString dumpInfo() const override {
2565 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002566 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002567 string.appendf(
2568 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2569 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002570 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002571 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2572 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2573 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002574 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002575 string += fHelper.dumpInfo();
2576 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002577 return string;
2578 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002579#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002580
Chris Dalton6ce447a2019-06-23 18:07:38 -06002581 GrProcessorSet::Analysis finalize(
2582 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2583 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002584 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002585 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002586 GrProcessorAnalysisCoverage::kSingleChannel, color,
2587 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002588 }
2589
2590 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2591
Brian Salomon92aee3d2016-12-21 09:20:25 -05002592private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002593 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002594 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002595 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002596 SkASSERT(smInset < bigInset);
2597
2598 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002599 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2600 color,
2601 xOffset, 0.0f,
2602 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002603
2604 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002605 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2606 color,
2607 xOffset, 0.0f,
2608 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002609
Brian Osmana1d4eb92018-12-06 16:33:10 -05002610 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2611 color,
2612 0.0f, 0.0f,
2613 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002614
Brian Osmana1d4eb92018-12-06 16:33:10 -05002615 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2616 color,
2617 0.0f, 0.0f,
2618 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002619
Brian Osmana1d4eb92018-12-06 16:33:10 -05002620 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2621 color,
2622 0.0f, 0.0f,
2623 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002624
Brian Osmana1d4eb92018-12-06 16:33:10 -05002625 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2626 color,
2627 0.0f, 0.0f,
2628 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002629
2630 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002631 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2632 color,
2633 xOffset, 0.0f,
2634 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002635
2636 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002637 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2638 color,
2639 xOffset, 0.0f,
2640 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002641 }
2642
Robert Phillips2669a7b2020-03-12 12:07:19 -04002643 GrProgramInfo* programInfo() override { return fProgramInfo; }
2644
Robert Phillips4133dc42020-03-11 15:55:55 -04002645 void onCreateProgramInfo(const GrCaps* caps,
2646 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002647 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002648 GrAppliedClip&& appliedClip,
2649 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002650 // Invert the view matrix as a local matrix (if any other processors require coords).
2651 SkMatrix localMatrix;
2652 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002653 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002654 }
2655
Robert Phillips4490d922020-03-03 14:50:59 -05002656 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002657 false, false, false, false,
2658 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002659
Brian Salomon8afde5f2020-04-01 16:22:00 -04002660 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002661 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002662 }
2663
Robert Phillips4490d922020-03-03 14:50:59 -05002664 void onPrepareDraws(Target* target) override {
2665 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002666 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002667 if (!fProgramInfo) {
2668 return;
2669 }
2670 }
2671
Brian Salomon12d22642019-01-29 14:38:50 -05002672 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002673 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002674
Robert Phillips4490d922020-03-03 14:50:59 -05002675 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2676 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002677 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002678 SkDebugf("Could not allocate vertices\n");
2679 return;
2680 }
2681
Brian Salomon12d22642019-01-29 14:38:50 -05002682 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002683 int firstIndex = 0;
2684 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2685 if (!indices) {
2686 SkDebugf("Could not allocate indices\n");
2687 return;
2688 }
2689
2690 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002691 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002692 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002693 SkScalar outerRadius = rrect.fOuterRadius;
2694 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002695
Brian Salomon289e3d82016-12-14 15:52:56 -05002696 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2697 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002698
Brian Salomon289e3d82016-12-14 15:52:56 -05002699 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002700 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002701 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002702 SkScalar innerRadius = rrect.fType != kFill_RRectType
2703 ? rrect.fInnerRadius / rrect.fOuterRadius
2704 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002705 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002706 verts.write(bounds.fLeft, yCoords[i],
2707 color,
2708 -1.0f, yOuterRadii[i],
2709 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002710
Brian Osmana1d4eb92018-12-06 16:33:10 -05002711 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2712 color,
2713 0.0f, yOuterRadii[i],
2714 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002715
Brian Osmana1d4eb92018-12-06 16:33:10 -05002716 verts.write(bounds.fRight - outerRadius, yCoords[i],
2717 color,
2718 0.0f, yOuterRadii[i],
2719 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002720
Brian Osmana1d4eb92018-12-06 16:33:10 -05002721 verts.write(bounds.fRight, yCoords[i],
2722 color,
2723 1.0f, yOuterRadii[i],
2724 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002725 }
jvanverthc3d0e422016-08-25 08:12:35 -07002726 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002727 // Effectively this is an additional stroked rrect, with its
2728 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2729 // This will give us correct AA in the center and the correct
2730 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002731 //
jvanvertha4f1af82016-08-29 07:17:47 -07002732 // Also, the outer offset is a constant vector pointing to the right, which
2733 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002734 if (kOverstroke_RRectType == rrect.fType) {
2735 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002736
Brian Salomon05441c42017-05-15 16:45:49 -04002737 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002738 // this is the normalized distance from the outer rectangle of this
2739 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002740 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002741
Brian Osmana1d4eb92018-12-06 16:33:10 -05002742 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002743 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002744 }
jvanverth6a397612016-08-26 08:15:33 -07002745
Brian Salomon05441c42017-05-15 16:45:49 -04002746 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2747 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002748 for (int i = 0; i < primIndexCount; ++i) {
2749 *indices++ = primIndices[i] + currStartVertex;
2750 }
2751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002753 }
2754
Robert Phillips4490d922020-03-03 14:50:59 -05002755 fMesh = target->allocMesh();
2756 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002757 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002758 }
2759
2760 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002761 if (!fProgramInfo || !fMesh) {
2762 return;
2763 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002764
Chris Dalton765ed362020-03-16 17:34:44 -06002765 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2766 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2767 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002768 }
2769
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002770 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2771 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002772 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002773
2774 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002775 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002776 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002777 }
2778
Brian Salomon05441c42017-05-15 16:45:49 -04002779 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002780 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002781 }
2782
Brian Salomon05441c42017-05-15 16:45:49 -04002783 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002784 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2785 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002786 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002787 }
2788
Brian Salomon05441c42017-05-15 16:45:49 -04002789 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002790 fVertCount += that->fVertCount;
2791 fIndexCount += that->fIndexCount;
2792 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002793 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002794 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002795 }
2796
Brian Salomon05441c42017-05-15 16:45:49 -04002797 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002798 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002799 SkScalar fInnerRadius;
2800 SkScalar fOuterRadius;
2801 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002802 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002803 };
2804
Brian Salomon289e3d82016-12-14 15:52:56 -05002805 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002806 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002807 int fVertCount;
2808 int fIndexCount;
2809 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002810 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002811 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002812
Chris Daltoneb694b72020-03-16 09:25:50 -06002813 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002814 GrProgramInfo* fProgramInfo = nullptr;
2815
Brian Salomon05441c42017-05-15 16:45:49 -04002816 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002817};
2818
jvanverth84839f62016-08-29 10:16:40 -07002819static const int kNumRRectsInIndexBuffer = 256;
2820
2821GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2822GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002823static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2824 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002825 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2826 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2827 switch (type) {
2828 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002829 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002830 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2831 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002832 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002833 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002834 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2835 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002836 default:
2837 SkASSERT(false);
2838 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002839 }
jvanverth84839f62016-08-29 10:16:40 -07002840}
2841
Brian Salomon05441c42017-05-15 16:45:49 -04002842class EllipticalRRectOp : public GrMeshDrawOp {
2843private:
2844 using Helper = GrSimpleMeshDrawOpHelper;
2845
joshualitt76e7fb62015-02-11 08:52:27 -08002846public:
Brian Salomon25a88092016-12-01 09:36:50 -05002847 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002848
bsalomon4b4a7cc2016-07-08 04:42:54 -07002849 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2850 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002851 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002852 GrPaint&& paint,
2853 const SkMatrix& viewMatrix,
2854 const SkRect& devRect,
2855 float devXRadius,
2856 float devYRadius,
2857 SkVector devStrokeWidths,
2858 bool strokeOnly) {
Brian Osman9fb7fa52019-07-01 16:48:39 -04002859 SkASSERT(devXRadius >= 0.5);
2860 SkASSERT(devYRadius >= 0.5);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002861 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2862 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002863 if (devStrokeWidths.fX > 0) {
2864 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2865 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2866 } else {
2867 devStrokeWidths.scale(SK_ScalarHalf);
2868 }
joshualitt76e7fb62015-02-11 08:52:27 -08002869
bsalomon4b4a7cc2016-07-08 04:42:54 -07002870 // we only handle thick strokes for near-circular ellipses
2871 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002872 (SK_ScalarHalf * devXRadius > devYRadius ||
2873 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002874 return nullptr;
2875 }
2876
2877 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002878 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2879 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002880 return nullptr;
2881 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002882 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2883 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002884 return nullptr;
2885 }
Brian Salomon05441c42017-05-15 16:45:49 -04002886 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002887 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002888 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002889 devXRadius, devYRadius, devStrokeWidths,
2890 strokeOnly);
2891 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002892
Greg Daniel2655ede2019-04-10 00:49:28 +00002893 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002894 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2895 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002896 : INHERITED(ClassID())
2897 , fHelper(helperArgs, GrAAType::kCoverage)
2898 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002899 SkScalar innerXRadius = 0.0f;
2900 SkScalar innerYRadius = 0.0f;
2901 SkRect bounds = devRect;
2902 bool stroked = false;
2903 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002904 // this is legit only if scale & translation (which should be the case at the moment)
2905 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002906 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2907 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002908 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2909 }
2910
Brian Salomon05441c42017-05-15 16:45:49 -04002911 devXRadius += devStrokeHalfWidths.fX;
2912 devYRadius += devStrokeHalfWidths.fY;
2913 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002914 }
2915
Brian Salomon05441c42017-05-15 16:45:49 -04002916 fStroked = stroked;
2917 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002918 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002919 // Expand the rect for aa in order to generate the correct vertices.
2920 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002921 fRRects.emplace_back(
2922 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002923 }
2924
Brian Salomon289e3d82016-12-14 15:52:56 -05002925 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002926
Chris Dalton1706cbf2019-05-21 19:35:29 -06002927 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002928 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002929 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002930 } else {
2931 fHelper.visitProxies(func);
2932 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002933 }
2934
Brian Osman9a390ac2018-11-12 09:47:48 -05002935#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002936 SkString dumpInfo() const override {
2937 SkString string;
2938 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002939 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002940 string.appendf(
2941 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2942 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002943 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002944 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2945 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002946 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002947 string += fHelper.dumpInfo();
2948 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002949 return string;
2950 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002951#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002952
Chris Dalton6ce447a2019-06-23 18:07:38 -06002953 GrProcessorSet::Analysis finalize(
2954 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2955 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002956 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002957 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002958 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002959 GrProcessorAnalysisCoverage::kSingleChannel, color,
2960 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002961 }
2962
2963 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2964
bsalomone46f9fe2015-08-18 06:05:14 -07002965private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002966 GrProgramInfo* programInfo() override { return fProgramInfo; }
2967
Robert Phillips4133dc42020-03-11 15:55:55 -04002968 void onCreateProgramInfo(const GrCaps* caps,
2969 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002970 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002971 GrAppliedClip&& appliedClip,
2972 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002973 SkMatrix localMatrix;
2974 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002975 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002976 }
2977
Robert Phillips4490d922020-03-03 14:50:59 -05002978 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2979 fUseScale, localMatrix);
2980
Brian Salomon8afde5f2020-04-01 16:22:00 -04002981 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002982 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002983 }
2984
Robert Phillips4490d922020-03-03 14:50:59 -05002985 void onPrepareDraws(Target* target) override {
2986 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002987 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002988 if (!fProgramInfo) {
2989 return;
2990 }
2991 }
joshualitt76e7fb62015-02-11 08:52:27 -08002992
bsalomonb5238a72015-05-05 07:49:49 -07002993 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002994 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002995 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2996 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002997
Brian Salomon12d22642019-01-29 14:38:50 -05002998 if (!indexBuffer) {
2999 SkDebugf("Could not allocate indices\n");
3000 return;
3001 }
Robert Phillips4490d922020-03-03 14:50:59 -05003002 PatternHelper helper(target, GrPrimitiveType::kTriangles,
3003 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05003004 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04003005 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05003006 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05003007 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08003008 SkDebugf("Could not allocate vertices\n");
3009 return;
3010 }
3011
Brian Salomon05441c42017-05-15 16:45:49 -04003012 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003013 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08003014 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05003015 float reciprocalRadii[4] = {
3016 SkScalarInvert(rrect.fXRadius),
3017 SkScalarInvert(rrect.fYRadius),
3018 SkScalarInvert(rrect.fInnerXRadius),
3019 SkScalarInvert(rrect.fInnerYRadius)
3020 };
joshualitt76e7fb62015-02-11 08:52:27 -08003021
3022 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003023 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3024 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003025
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003026 SkScalar xMaxOffset = xOuterRadius;
3027 SkScalar yMaxOffset = yOuterRadius;
3028 if (!fStroked) {
3029 // For filled rrects we map a unit circle in the vertex attributes rather than
3030 // computing an ellipse and modifying that distance, so we normalize to 1.
3031 xMaxOffset /= rrect.fXRadius;
3032 yMaxOffset /= rrect.fYRadius;
3033 }
3034
Brian Salomon05441c42017-05-15 16:45:49 -04003035 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003036
Brian Salomon289e3d82016-12-14 15:52:56 -05003037 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3038 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003039 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003040 SK_ScalarNearlyZero, // we're using inversesqrt() in
3041 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003042 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003043
Brian Osman788b9162020-02-07 10:36:46 -05003044 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003045 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003046 verts.write(bounds.fLeft, yCoords[i],
3047 color,
3048 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003049 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003050 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003051
Brian Osmana1d4eb92018-12-06 16:33:10 -05003052 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3053 color,
3054 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003055 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003056 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003057
Brian Osmana1d4eb92018-12-06 16:33:10 -05003058 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3059 color,
3060 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003061 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003062 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003063
Brian Osmana1d4eb92018-12-06 16:33:10 -05003064 verts.write(bounds.fRight, yCoords[i],
3065 color,
3066 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003067 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003068 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003069 }
3070 }
Robert Phillips4490d922020-03-03 14:50:59 -05003071 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003072 }
3073
3074 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003075 if (!fProgramInfo || !fMesh) {
3076 return;
3077 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003078
Chris Dalton765ed362020-03-16 17:34:44 -06003079 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3080 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3081 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003082 }
3083
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003084 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3085 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003086 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003087
Brian Salomon05441c42017-05-15 16:45:49 -04003088 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003089 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003090 }
3091
bsalomoncdaa97b2016-03-08 08:30:14 -08003092 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003093 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003094 }
3095
Brian Salomon05441c42017-05-15 16:45:49 -04003096 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003097 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3098 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003099 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003100 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003101
Brian Salomon05441c42017-05-15 16:45:49 -04003102 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003103 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003104 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003105 }
3106
Brian Salomon05441c42017-05-15 16:45:49 -04003107 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003108 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003109 SkScalar fXRadius;
3110 SkScalar fYRadius;
3111 SkScalar fInnerXRadius;
3112 SkScalar fInnerYRadius;
3113 SkRect fDevBounds;
3114 };
3115
Brian Salomon289e3d82016-12-14 15:52:56 -05003116 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003117 Helper fHelper;
3118 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003119 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003120 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003121 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003122
Chris Daltoneb694b72020-03-16 09:25:50 -06003123 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003124 GrProgramInfo* fProgramInfo = nullptr;
3125
Brian Salomon05441c42017-05-15 16:45:49 -04003126 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003127};
3128
Jim Van Verth64b85892019-06-17 12:01:46 -04003129std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3130 GrPaint&& paint,
3131 const SkMatrix& viewMatrix,
3132 const SkRRect& rrect,
3133 const SkStrokeRec& stroke,
3134 const GrShaderCaps* shaderCaps) {
3135 SkASSERT(viewMatrix.rectStaysRect());
3136 SkASSERT(viewMatrix.isSimilarity());
3137 SkASSERT(rrect.isSimple());
3138 SkASSERT(!rrect.isOval());
3139 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3140
3141 // RRect ops only handle simple, but not too simple, rrects.
3142 // Do any matrix crunching before we reset the draw state for device coords.
3143 const SkRect& rrectBounds = rrect.getBounds();
3144 SkRect bounds;
3145 viewMatrix.mapRect(&bounds, rrectBounds);
3146
3147 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3148 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3149 viewMatrix[SkMatrix::kMSkewY]));
3150
3151 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3152 SkScalar scaledStroke = -1;
3153 SkScalar strokeWidth = stroke.getWidth();
3154 SkStrokeRec::Style style = stroke.getStyle();
3155
3156 bool isStrokeOnly =
3157 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3158 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3159
3160 if (hasStroke) {
3161 if (SkStrokeRec::kHairline_Style == style) {
3162 scaledStroke = SK_Scalar1;
3163 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003164 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3165 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003166 }
3167 }
3168
3169 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3170 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3171 // patch will have fractional coverage. This only matters when the interior is actually filled.
3172 // We could consider falling back to rect rendering here, since a tiny radius is
3173 // indistinguishable from a square corner.
3174 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3175 return nullptr;
3176 }
3177
3178 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3179 scaledStroke, isStrokeOnly);
3180}
3181
Robert Phillipsb97da532019-02-12 15:24:12 -05003182static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003183 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003184 const SkMatrix& viewMatrix,
3185 const SkRRect& rrect,
3186 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003187 SkASSERT(viewMatrix.rectStaysRect());
3188 SkASSERT(rrect.isSimple());
3189 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003190
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003191 // RRect ops only handle simple, but not too simple, rrects.
3192 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003194 SkRect bounds;
3195 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003196
Mike Reed242135a2018-02-22 13:41:39 -05003197 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003198 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3199 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3200 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3201 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003202
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003203 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003204
bsalomon4b4a7cc2016-07-08 04:42:54 -07003205 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3206 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003207 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003208
Brian Salomon289e3d82016-12-14 15:52:56 -05003209 bool isStrokeOnly =
3210 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003211 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3212
3213 if (hasStroke) {
3214 if (SkStrokeRec::kHairline_Style == style) {
3215 scaledStroke.set(1, 1);
3216 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003217 scaledStroke.fX = SkScalarAbs(
3218 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3219 scaledStroke.fY = SkScalarAbs(
3220 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003221 }
3222
Jim Van Verth64b85892019-06-17 12:01:46 -04003223 // if half of strokewidth is greater than radius, we don't handle that right now
3224 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3225 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003226 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003227 }
3228 }
3229
Brian Salomon8a97f562019-04-18 14:07:27 -04003230 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003231 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003232 std::swap(xRadius, yRadius);
3233 std::swap(scaledStroke.fX, scaledStroke.fY);
3234 }
3235
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003236 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3237 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3238 // patch will have fractional coverage. This only matters when the interior is actually filled.
3239 // We could consider falling back to rect rendering here, since a tiny radius is
3240 // indistinguishable from a square corner.
3241 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003242 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003243 }
3244
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003245 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003246 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3247 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003248}
3249
Robert Phillipsb97da532019-02-12 15:24:12 -05003250std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003251 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003252 const SkMatrix& viewMatrix,
3253 const SkRRect& rrect,
3254 const SkStrokeRec& stroke,
3255 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003256 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003257 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003258 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003259 }
3260
3261 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003262 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003263 }
3264
Greg Daniel2655ede2019-04-10 00:49:28 +00003265 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003266}
joshualitt3e708c52015-04-30 13:49:27 -07003267
bsalomon4b4a7cc2016-07-08 04:42:54 -07003268///////////////////////////////////////////////////////////////////////////////
3269
Jim Van Verth64b85892019-06-17 12:01:46 -04003270std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3271 GrPaint&& paint,
3272 const SkMatrix& viewMatrix,
3273 const SkRect& oval,
3274 const GrStyle& style,
3275 const GrShaderCaps* shaderCaps) {
3276 SkScalar width = oval.width();
3277 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3278 circle_stays_circle(viewMatrix));
3279
3280 auto r = width / 2.f;
3281 SkPoint center = { oval.centerX(), oval.centerY() };
3282 if (style.hasNonDashPathEffect()) {
3283 return nullptr;
3284 } else if (style.isDashed()) {
3285 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3286 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3287 return nullptr;
3288 }
3289 auto onInterval = style.dashIntervals()[0];
3290 auto offInterval = style.dashIntervals()[1];
3291 if (offInterval == 0) {
3292 GrStyle strokeStyle(style.strokeRec(), nullptr);
3293 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3294 strokeStyle, shaderCaps);
3295 } else if (onInterval == 0) {
3296 // There is nothing to draw but we have no way to indicate that here.
3297 return nullptr;
3298 }
3299 auto angularOnInterval = onInterval / r;
3300 auto angularOffInterval = offInterval / r;
3301 auto phaseAngle = style.dashPhase() / r;
3302 // Currently this function doesn't accept ovals with different start angles, though
3303 // it could.
3304 static const SkScalar kStartAngle = 0.f;
3305 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3306 style.strokeRec().getWidth(), kStartAngle,
3307 angularOnInterval, angularOffInterval, phaseAngle);
3308 }
3309 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3310}
3311
Robert Phillipsb97da532019-02-12 15:24:12 -05003312std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003313 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003314 const SkMatrix& viewMatrix,
3315 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003316 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003317 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003318 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003319 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003320 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3321 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003322 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003323 }
3324
3325 if (style.pathEffect()) {
3326 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003327 }
3328
Stan Ilieveb868aa2017-02-21 11:06:16 -05003329 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003330 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003331 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003332 }
3333
Stan Ilieveb868aa2017-02-21 11:06:16 -05003334 // Otherwise, if we have shader derivative support, render as device-independent
3335 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003336 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3337 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3338 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3339 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3340 // Check for near-degenerate matrix
3341 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003342 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003343 style.strokeRec());
3344 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003345 }
3346
bsalomon4b4a7cc2016-07-08 04:42:54 -07003347 return nullptr;
3348}
3349
3350///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003351
Robert Phillipsb97da532019-02-12 15:24:12 -05003352std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003353 GrPaint&& paint,
3354 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003355 const SkRect& oval, SkScalar startAngle,
3356 SkScalar sweepAngle, bool useCenter,
3357 const GrStyle& style,
3358 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003359 SkASSERT(!oval.isEmpty());
3360 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003361 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003362 if (SkScalarAbs(sweepAngle) >= 360.f) {
3363 return nullptr;
3364 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003365 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3366 return nullptr;
3367 }
3368 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003369 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3370 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003371 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003372 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003373}
3374
3375///////////////////////////////////////////////////////////////////////////////
3376
Hal Canary6f6961e2017-01-31 13:50:44 -05003377#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003378
Brian Salomon05441c42017-05-15 16:45:49 -04003379GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003380 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003381 SkScalar rotate = random->nextSScalar1() * 360.f;
3382 SkScalar translateX = random->nextSScalar1() * 1000.f;
3383 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003384 SkScalar scale;
3385 do {
3386 scale = random->nextSScalar1() * 100.f;
3387 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003388 SkMatrix viewMatrix;
3389 viewMatrix.setRotate(rotate);
3390 viewMatrix.postTranslate(translateX, translateY);
3391 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 SkRect circle = GrTest::TestSquare(random);
3393 SkPoint center = {circle.centerX(), circle.centerY()};
3394 SkScalar radius = circle.width() / 2.f;
3395 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003396 CircleOp::ArcParams arcParamsTmp;
3397 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003398 if (random->nextBool()) {
3399 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003400 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3401 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003402 arcParams = &arcParamsTmp;
3403 }
Greg Daniel2655ede2019-04-10 00:49:28 +00003404 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003405 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003406 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003407 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003408 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003409 }
Mike Klein16885072018-12-11 09:54:31 -05003410 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003411 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003412}
3413
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003414GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3415 SkScalar rotate = random->nextSScalar1() * 360.f;
3416 SkScalar translateX = random->nextSScalar1() * 1000.f;
3417 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003418 SkScalar scale;
3419 do {
3420 scale = random->nextSScalar1() * 100.f;
3421 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003422 SkMatrix viewMatrix;
3423 viewMatrix.setRotate(rotate);
3424 viewMatrix.postTranslate(translateX, translateY);
3425 viewMatrix.postScale(scale, scale);
3426 SkRect circle = GrTest::TestSquare(random);
3427 SkPoint center = {circle.centerX(), circle.centerY()};
3428 SkScalar radius = circle.width() / 2.f;
3429 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3430 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3431 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3432 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3433 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003434 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3435 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003436 startAngle, onAngle, offAngle, phase);
3437}
3438
Brian Salomon05441c42017-05-15 16:45:49 -04003439GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003440 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003441 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003442 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003443 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003444}
3445
Brian Salomon05441c42017-05-15 16:45:49 -04003446GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003447 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003448 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003449 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003450 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003451}
3452
Jim Van Verth64b85892019-06-17 12:01:46 -04003453GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3454 do {
3455 SkScalar rotate = random->nextSScalar1() * 360.f;
3456 SkScalar translateX = random->nextSScalar1() * 1000.f;
3457 SkScalar translateY = random->nextSScalar1() * 1000.f;
3458 SkScalar scale;
3459 do {
3460 scale = random->nextSScalar1() * 100.f;
3461 } while (scale == 0);
3462 SkMatrix viewMatrix;
3463 viewMatrix.setRotate(rotate);
3464 viewMatrix.postTranslate(translateX, translateY);
3465 viewMatrix.postScale(scale, scale);
3466 SkRect rect = GrTest::TestRect(random);
3467 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3468 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3469 if (rrect.isOval()) {
3470 continue;
3471 }
3472 std::unique_ptr<GrDrawOp> op =
3473 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3474 GrTest::TestStrokeRec(random), nullptr);
3475 if (op) {
3476 return op;
3477 }
3478 assert_alive(paint);
3479 } while (true);
3480}
3481
Brian Salomon05441c42017-05-15 16:45:49 -04003482GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003483 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003484 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003485 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003486 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003487}
3488
3489#endif