blob: 9e0022230b110455c799f78c500abe4f19b8b556 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
70 return arena->make<CircleGeometryProcessor>(stroke, clipPlane, isectPlane, unionPlane,
71 roundCaps, wideColor, localMatrix);
72 }
73
74 const char* name() const override { return "CircleGeometryProcessor"; }
75
76 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
77 GLSLProcessor::GenKey(*this, caps, b);
78 }
79
80 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
81 return new GLSLProcessor();
82 }
83
84private:
85 friend class ::SkArenaAlloc; // for access to ctor
86
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800119 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500155 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800156
joshualittabb52a12015-01-13 15:02:10 -0800157 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500158 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400159 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
160 cgp.fInPosition.asShaderVar(), cgp.fLocalMatrix,
161 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800162
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400163 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500164 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000165 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800166 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500167 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500168 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000169 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800170 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000172
Brian Salomon92be2f72018-06-19 14:33:47 -0400173 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500174 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000175 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
176 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400177 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000179 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
180 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700181 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
185 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
187 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400188 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400189 // We compute coverage of the round caps as circles at the butt caps produced
190 // by the clip planes. The inverse of the clip planes is applied so that there
191 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400192 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
194 " roundCapCenters.xy)));"
195 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
196 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400197 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400198 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
199 capRadius.fsIn(), capRadius.fsIn());
200 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700201 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000202 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 }
204
robertphillips46d36f02015-01-18 08:14:14 -0800205 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500206 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700207 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800208 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400209 uint32_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500210 key = cgp.fStroke ? 0x01 : 0x0;
Michael Ludwig553db622020-06-19 10:47:30 -0400211 key |= cgp.fInClipPlane.isInitialized() ? 0x02 : 0x0;
212 key |= cgp.fInIsectPlane.isInitialized() ? 0x04 : 0x0;
213 key |= cgp.fInUnionPlane.isInitialized() ? 0x08 : 0x0;
214 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x10 : 0x0;
215 key |= (ComputeMatrixKey(cgp.fLocalMatrix) << 16);
joshualittb8c241a2015-05-19 08:23:30 -0700216 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217 }
218
Brian Osman609f1592020-07-01 15:14:39 -0400219 void setData(const GrGLSLProgramDataManager& pdman,
220 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400221 this->setTransform(pdman, fLocalMatrixUniform,
222 primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
223 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700224 }
225
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400227 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400228
229 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
230 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 };
232
Brian Salomon289e3d82016-12-14 15:52:56 -0500233 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400234
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500235 Attribute fInPosition;
236 Attribute fInColor;
237 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400238 // Optional attributes.
239 Attribute fInClipPlane;
240 Attribute fInIsectPlane;
241 Attribute fInUnionPlane;
242 Attribute fInRoundCapCenters;
243
Brian Salomon289e3d82016-12-14 15:52:56 -0500244 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400245 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246
John Stiles7571f9e2020-09-02 22:42:33 -0400247 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248};
249
bsalomoncdaa97b2016-03-08 08:30:14 -0800250GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
Hal Canary6f6961e2017-01-31 13:50:44 -0500252#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500253GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400254 bool stroke = d->fRandom->nextBool();
255 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500256 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400257 bool clipPlane = d->fRandom->nextBool();
258 bool isectPlane = d->fRandom->nextBool();
259 bool unionPlane = d->fRandom->nextBool();
260 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
262 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263}
Hal Canary6f6961e2017-01-31 13:50:44 -0500264#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400266class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
267public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500268 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
269 const SkMatrix& localMatrix) {
270 return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400271 }
272
273 ~ButtCapDashedCircleGeometryProcessor() override {}
274
275 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
276
277 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
278 GLSLProcessor::GenKey(*this, caps, b);
279 }
280
281 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
282 return new GLSLProcessor();
283 }
284
285private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500286 friend class ::SkArenaAlloc; // for access to ctor
287
288 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
289 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
290 , fLocalMatrix(localMatrix) {
291 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
292 fInColor = MakeColorAttribute("inColor", wideColor);
293 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
295 this->setVertexAttributes(&fInPosition, 4);
296 }
297
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298 class GLSLProcessor : public GrGLSLGeometryProcessor {
299 public:
300 GLSLProcessor() {}
301
302 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
303 const ButtCapDashedCircleGeometryProcessor& bcscgp =
304 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
305 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
306 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
307 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
308 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
309
310 // emit attributes
311 varyingHandler->emitAttributes(bcscgp);
312 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500313 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314
315 fragBuilder->codeAppend("float4 dashParams;");
316 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500317 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400318 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
319 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
320 varyingHandler->addVarying("wrapDashes", &wrapDashes,
321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
322 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
323 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
324 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
327 // x = length of on interval, y = length of on + off.
328 // There are two other parameters in dashParams.zw:
329 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
330 // Each interval has a "corresponding" dash which may be shifted partially or
331 // fully out of its interval by the phase. So there may be up to two "visual"
332 // dashes in an interval.
333 // When computing coverage in an interval we look at three dashes. These are the
334 // "corresponding" dashes from the current, previous, and next intervals. Any of these
335 // may be phase shifted into our interval or even when phase=0 they may be within half a
336 // pixel distance of a pixel center in the interval.
337 // When in the first interval we need to check the dash from the last interval. And
338 // similarly when in the last interval we need to check the dash from the first
339 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
340 // We compute the dash begin/end angles in the vertex shader and apply them in the
341 // fragment shader when we detect we're in the first/last interval.
342 vertBuilder->codeAppend(R"(
343 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
344 // to the fragment shader as a varying.
345 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500346 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400347 // We can happen to be perfectly divisible.
348 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500349 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400350 }
351 // Let 'l' be the last interval before reaching 2 pi.
352 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
353 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
354 // interval.
355 half offset = 0;
356 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500359 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400360 }
361 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
362 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
363 // min.
364 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
365
366 // Based on the phase determine whether the -1st, 0th, or 1st interval's
367 // "corresponding" dash appears in the 0th interval and is closest to l.
368 offset = 0;
369 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500370 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400371 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500372 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400373 }
374 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
375 wrapDashes.w = wrapDashes.z + dashParams.x;
376 // The start of the dash we're considering may be clipped by the start of the
377 // circle.
378 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
379 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
382 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
383 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
384
385 // setup pass through color
386 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500387 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
389
390 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500391 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400392 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
393 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
394 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
400 SkString fnName;
401 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
402 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400406 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400407 )",
408 &fnName);
409 fragBuilder->codeAppend(R"(
410 float d = length(circleEdge.xy) * circleEdge.z;
411
412 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500413 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400414 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500415 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400416 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400417 edgeAlpha *= innerAlpha;
418
Ethan Nicholase1f55022019-02-05 17:17:40 -0500419 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400420 angleFromStart = mod(angleFromStart, 6.28318530718);
421 float x = mod(angleFromStart, dashParams.y);
422 // Convert the radial distance from center to pixel into a diameter.
423 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500424 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
425 half(dashParams.w));
426 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
427 half(dashParams.y) + half(dashParams.x) -
428 half(dashParams.w));
429 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
430 half(-dashParams.y) + half(dashParams.x) -
431 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 half dashAlpha = 0;
433 )");
434 fragBuilder->codeAppendf(R"(
435 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400437 currDash.y = min(currDash.y, lastIntervalLength);
438 if (nextDash.x >= lastIntervalLength) {
439 // The next dash is outside the 0..2pi range, throw it away
440 nextDash.xy = half2(1000);
441 } else {
442 // Clip the end of the next dash to the end of the circle
443 nextDash.y = min(nextDash.y, lastIntervalLength);
444 }
445 }
446 )", fnName.c_str(), fnName.c_str());
447 fragBuilder->codeAppendf(R"(
448 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500449 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400450 currDash.x = max(currDash.x, 0);
451 if (prevDash.y <= 0) {
452 // The previous dash is outside the 0..2pi range, throw it away
453 prevDash.xy = half2(1000);
454 } else {
455 // Clip the start previous dash to the start of the circle
456 prevDash.x = max(prevDash.x, 0);
457 }
458 }
459 )", fnName.c_str(), fnName.c_str());
460 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
462 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
463 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400464 dashAlpha = min(dashAlpha, 1);
465 edgeAlpha *= dashAlpha;
466 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
467 fnName.c_str());
468 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
469 }
470
471 static void GenKey(const GrGeometryProcessor& gp,
472 const GrShaderCaps&,
473 GrProcessorKeyBuilder* b) {
474 const ButtCapDashedCircleGeometryProcessor& bcscgp =
475 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400476 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400477 }
478
Brian Osman609f1592020-07-01 15:14:39 -0400479 void setData(const GrGLSLProgramDataManager& pdman,
480 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400481 this->setTransform(pdman, fLocalMatrixUniform,
482 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
483 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400484 }
485
486 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400487 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400488
489 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
490 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 };
492
493 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500494 Attribute fInPosition;
495 Attribute fInColor;
496 Attribute fInCircleEdge;
497 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400498
499 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
500
John Stiles7571f9e2020-09-02 22:42:33 -0400501 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400502};
503
504#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500505GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500506 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400507 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500508 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400509}
510#endif
511
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000512///////////////////////////////////////////////////////////////////////////////
513
514/**
515 * 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 +0000516 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
517 * in both x and y directions.
518 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000519 * 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 +0000520 */
521
bsalomoncdaa97b2016-03-08 08:30:14 -0800522class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000523public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500524 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
525 bool useScale, const SkMatrix& localMatrix) {
526 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
527 }
528
529 ~EllipseGeometryProcessor() override {}
530
531 const char* name() const override { return "EllipseGeometryProcessor"; }
532
533 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
534 GLSLProcessor::GenKey(*this, caps, b);
535 }
536
537 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
538 return new GLSLProcessor();
539 }
540
541private:
542 friend class ::SkArenaAlloc; // for access to ctor
543
Greg Daniel2655ede2019-04-10 00:49:28 +0000544 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400545 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500546 : INHERITED(kEllipseGeometryProcessor_ClassID)
547 , fLocalMatrix(localMatrix)
548 , fStroke(stroke)
549 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500550 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500551 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400552 if (useScale) {
553 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
554 } else {
555 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
556 }
557 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500558 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 }
560
egdaniel57d3b032015-11-13 11:57:27 -0800561 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800563 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000564
Brian Salomon289e3d82016-12-14 15:52:56 -0500565 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800566 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800567 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800568 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800569 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000570
joshualittabb52a12015-01-13 15:02:10 -0800571 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800572 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800573
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400574 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
575 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800576 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800577 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500578 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400580 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800581 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500582 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800583
Chris Dalton60283612018-02-14 13:38:14 -0700584 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700585 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500586 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800587
joshualittabb52a12015-01-13 15:02:10 -0800588 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500589 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400590 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
591 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
592 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800593
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400594 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
595 // to compute both the edges because we need two separate test equations for
596 // the single offset.
597 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
598 // the distance by the gradient, non-uniformly scaled by the inverse of the
599 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800600
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400601 // On medium precision devices, we scale the denominator of the distance equation
602 // before taking the inverse square root to minimize the chance that we're dividing
603 // by zero, then we scale the result back.
604
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000605 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400606 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400607 if (egp.fStroke) {
608 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
609 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400610 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
611 if (egp.fUseScale) {
612 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
613 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
614 } else {
615 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
616 }
617 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700618
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000619 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400620 if (args.fShaderCaps->floatIs32Bits()) {
621 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
622 } else {
623 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
624 }
625 if (egp.fUseScale) {
626 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
627 ellipseOffsets.fsIn());
628 } else {
629 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
630 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000631 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000632
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000633 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800634 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800636 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400637 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400638 if (egp.fUseScale) {
639 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
640 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
641 } else {
642 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
643 }
644 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
645 if (!args.fShaderCaps->floatIs32Bits()) {
646 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
647 }
648 if (egp.fUseScale) {
649 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
650 ellipseOffsets.fsIn());
651 } else {
652 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
653 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000654 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655 }
656
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400657 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658 }
659
robertphillips46d36f02015-01-18 08:14:14 -0800660 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500661 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700662 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800663 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400664 uint32_t key = egp.fStroke ? 0x1 : 0x0;
665 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700666 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667 }
668
Brian Osman609f1592020-07-01 15:14:39 -0400669 void setData(const GrGLSLProgramDataManager& pdman,
670 const GrPrimitiveProcessor& primProc) override {
bsalomona624bf32016-09-20 09:12:47 -0700671 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400672 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700673 }
674
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000675 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400676 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400677
678 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
679 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 };
681
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500682 Attribute fInPosition;
683 Attribute fInColor;
684 Attribute fInEllipseOffset;
685 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400686
joshualitte3ababe2015-05-15 07:56:07 -0700687 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000688 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400689 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400691 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000692
John Stiles7571f9e2020-09-02 22:42:33 -0400693 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000694};
695
bsalomoncdaa97b2016-03-08 08:30:14 -0800696GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000697
Hal Canary6f6961e2017-01-31 13:50:44 -0500698#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500699GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
700 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
701 d->fRandom->nextBool(), d->fRandom->nextBool(),
702 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000703}
Hal Canary6f6961e2017-01-31 13:50:44 -0500704#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000705
706///////////////////////////////////////////////////////////////////////////////
707
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000708/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000709 * 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 +0000710 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
711 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
712 * using differentials.
713 *
714 * The result is device-independent and can be used with any affine matrix.
715 */
716
bsalomoncdaa97b2016-03-08 08:30:14 -0800717enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000718
bsalomoncdaa97b2016-03-08 08:30:14 -0800719class DIEllipseGeometryProcessor : public GrGeometryProcessor {
720public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500721 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
722 const SkMatrix& viewMatrix, DIEllipseStyle style) {
723 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
724 }
725
726 ~DIEllipseGeometryProcessor() override {}
727
728 const char* name() const override { return "DIEllipseGeometryProcessor"; }
729
730 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
731 GLSLProcessor::GenKey(*this, caps, b);
732 }
733
734 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
735 return new GLSLProcessor();
736 }
737
738private:
739 friend class ::SkArenaAlloc; // for access to ctor
740
Greg Daniel2655ede2019-04-10 00:49:28 +0000741 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400742 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400743 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400744 , fViewMatrix(viewMatrix)
745 , fUseScale(useScale)
746 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500747 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500748 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400749 if (useScale) {
750 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
751 kFloat3_GrSLType};
752 } else {
753 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
754 kFloat2_GrSLType};
755 }
756 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500757 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000758 }
759
egdaniel57d3b032015-11-13 11:57:27 -0800760 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000761 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500762 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763
joshualitt465283c2015-09-11 08:19:35 -0700764 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800765 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800766 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800767 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800768 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000769
joshualittabb52a12015-01-13 15:02:10 -0800770 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800771 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800772
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400773 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
774 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800775 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500776 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700777
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400778 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800779 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500780 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800781
Chris Dalton60283612018-02-14 13:38:14 -0700782 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800784
joshualittabb52a12015-01-13 15:02:10 -0800785 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400786 this->writeOutputPosition(vertBuilder,
787 uniformHandler,
788 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500789 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400790 diegp.fViewMatrix,
791 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400792 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800793
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000794 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400795 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
796 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
797 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
798 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500799 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400800 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
801 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500802 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 if (diegp.fUseScale) {
804 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
805 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000806
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400807 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000808 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 if (args.fShaderCaps->floatIs32Bits()) {
810 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
811 } else {
812 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
813 }
814 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
815 if (diegp.fUseScale) {
816 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
817 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800818 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000819 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000820 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
821 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000823 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000824 }
825
826 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800827 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800828 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
829 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400830 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
831 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500832 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
834 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500835 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400836 if (diegp.fUseScale) {
837 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
838 }
839 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
840 if (!args.fShaderCaps->floatIs32Bits()) {
841 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
842 }
843 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
844 if (diegp.fUseScale) {
845 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
846 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000847 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000848 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000849
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400850 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000851 }
852
robertphillips46d36f02015-01-18 08:14:14 -0800853 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500854 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700855 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800856 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400857 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
858 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700859 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000860 }
861
Brian Osman609f1592020-07-01 15:14:39 -0400862 void setData(const GrGLSLProgramDataManager& pdman,
863 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800864 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700865
Michael Ludwig553db622020-06-19 10:47:30 -0400866 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000867 }
868
869 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400870 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700871 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800872
John Stiles7571f9e2020-09-02 22:42:33 -0400873 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000874 };
875
Brian Salomon92be2f72018-06-19 14:33:47 -0400876
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500877 Attribute fInPosition;
878 Attribute fInColor;
879 Attribute fInEllipseOffsets0;
880 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400881
Brian Salomon289e3d82016-12-14 15:52:56 -0500882 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400883 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500884 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000885
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400886 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000887
John Stiles7571f9e2020-09-02 22:42:33 -0400888 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889};
890
bsalomoncdaa97b2016-03-08 08:30:14 -0800891GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000892
Hal Canary6f6961e2017-01-31 13:50:44 -0500893#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500894GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
895 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
896 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
897 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000898}
Hal Canary6f6961e2017-01-31 13:50:44 -0500899#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000900
901///////////////////////////////////////////////////////////////////////////////
902
jvanverth6ca48822016-10-07 06:57:32 -0700903// We have two possible cases for geometry for a circle:
904
905// In the case of a normal fill, we draw geometry for the circle as an octagon.
906static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500907 // enter the octagon
908 // clang-format off
909 0, 1, 8, 1, 2, 8,
910 2, 3, 8, 3, 4, 8,
911 4, 5, 8, 5, 6, 8,
912 6, 7, 8, 7, 0, 8
913 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700914};
915
916// For stroked circles, we use two nested octagons.
917static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500918 // enter the octagon
919 // clang-format off
920 0, 1, 9, 0, 9, 8,
921 1, 2, 10, 1, 10, 9,
922 2, 3, 11, 2, 11, 10,
923 3, 4, 12, 3, 12, 11,
924 4, 5, 13, 4, 13, 12,
925 5, 6, 14, 5, 14, 13,
926 6, 7, 15, 6, 15, 14,
927 7, 0, 8, 7, 8, 15,
928 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700929};
930
Brian Osman9d958b52018-11-13 12:46:56 -0500931// Normalized geometry for octagons that circumscribe and lie on a circle:
932
933static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
934static constexpr SkPoint kOctagonOuter[] = {
935 SkPoint::Make(-kOctOffset, -1),
936 SkPoint::Make( kOctOffset, -1),
937 SkPoint::Make( 1, -kOctOffset),
938 SkPoint::Make( 1, kOctOffset),
939 SkPoint::Make( kOctOffset, 1),
940 SkPoint::Make(-kOctOffset, 1),
941 SkPoint::Make(-1, kOctOffset),
942 SkPoint::Make(-1, -kOctOffset),
943};
944
945// cosine and sine of pi/8
946static constexpr SkScalar kCosPi8 = 0.923579533f;
947static constexpr SkScalar kSinPi8 = 0.382683432f;
948static constexpr SkPoint kOctagonInner[] = {
949 SkPoint::Make(-kSinPi8, -kCosPi8),
950 SkPoint::Make( kSinPi8, -kCosPi8),
951 SkPoint::Make( kCosPi8, -kSinPi8),
952 SkPoint::Make( kCosPi8, kSinPi8),
953 SkPoint::Make( kSinPi8, kCosPi8),
954 SkPoint::Make(-kSinPi8, kCosPi8),
955 SkPoint::Make(-kCosPi8, kSinPi8),
956 SkPoint::Make(-kCosPi8, -kSinPi8),
957};
Brian Salomon289e3d82016-12-14 15:52:56 -0500958
jvanverth6ca48822016-10-07 06:57:32 -0700959static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
960static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
961static const int kVertsPerStrokeCircle = 16;
962static const int kVertsPerFillCircle = 9;
963
964static int circle_type_to_vert_count(bool stroked) {
965 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
966}
967
968static int circle_type_to_index_count(bool stroked) {
969 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
970}
971
972static const uint16_t* circle_type_to_indices(bool stroked) {
973 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
974}
975
976///////////////////////////////////////////////////////////////////////////////
977
Brian Salomon05441c42017-05-15 16:45:49 -0400978class CircleOp final : public GrMeshDrawOp {
979private:
980 using Helper = GrSimpleMeshDrawOpHelper;
981
joshualitt76e7fb62015-02-11 08:52:27 -0800982public:
Brian Salomon25a88092016-12-01 09:36:50 -0500983 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700984
bsalomon4f3a0ca2016-08-22 13:14:26 -0700985 /** Optional extra params to render a partial arc rather than a full circle. */
986 struct ArcParams {
987 SkScalar fStartAngleRadians;
988 SkScalar fSweepAngleRadians;
989 bool fUseCenter;
990 };
Brian Salomon05441c42017-05-15 16:45:49 -0400991
Herb Derbyc76d4092020-10-07 16:46:15 -0400992 static GrOp::Owner Make(GrRecordingContext* context,
993 GrPaint&& paint,
994 const SkMatrix& viewMatrix,
995 SkPoint center,
996 SkScalar radius,
997 const GrStyle& style,
998 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001000 if (style.hasPathEffect()) {
1001 return nullptr;
1002 }
Brian Salomon05441c42017-05-15 16:45:49 -04001003 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001004 SkStrokeRec::Style recStyle = stroke.getStyle();
1005 if (arcParams) {
1006 // Arc support depends on the style.
1007 switch (recStyle) {
1008 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001009 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001010 return nullptr;
1011 case SkStrokeRec::kFill_Style:
1012 // This supports all fills.
1013 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001014 case SkStrokeRec::kStroke_Style:
1015 // Strokes that don't use the center point are supported with butt and round
1016 // caps.
1017 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1018 return nullptr;
1019 }
1020 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001021 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001022 // Hairline only supports butt cap. Round caps could be emulated by slightly
1023 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001024 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1025 return nullptr;
1026 }
1027 break;
1028 }
1029 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001030 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1031 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001032 }
1033
Herb Derbyc76d4092020-10-07 16:46:15 -04001034 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001035 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1036 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001037 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001038 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001039 const SkStrokeRec& stroke = style.strokeRec();
1040 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041
Brian Salomon45c92202018-04-10 10:53:58 -04001042 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001043
bsalomon4b4a7cc2016-07-08 04:42:54 -07001044 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001045 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001046 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001047
Brian Salomon289e3d82016-12-14 15:52:56 -05001048 bool isStrokeOnly =
1049 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001050 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001051
jvanverth6ca48822016-10-07 06:57:32 -07001052 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001053 SkScalar outerRadius = radius;
1054 SkScalar halfWidth = 0;
1055 if (hasStroke) {
1056 if (SkScalarNearlyZero(strokeWidth)) {
1057 halfWidth = SK_ScalarHalf;
1058 } else {
1059 halfWidth = SkScalarHalf(strokeWidth);
1060 }
1061
1062 outerRadius += halfWidth;
1063 if (isStrokeOnly) {
1064 innerRadius = radius - halfWidth;
1065 }
1066 }
1067
1068 // The radii are outset for two reasons. First, it allows the shader to simply perform
1069 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1070 // Second, the outer radius is used to compute the verts of the bounding box that is
1071 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001072 outerRadius += SK_ScalarHalf;
1073 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001074 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001075 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001076
bsalomon4f3a0ca2016-08-22 13:14:26 -07001077 // This makes every point fully inside the intersection plane.
1078 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1079 // This makes every point fully outside the union plane.
1080 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001081 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001082 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1083 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001084 if (arcParams) {
1085 // The shader operates in a space where the circle is translated to be centered at the
1086 // origin. Here we compute points on the unit circle at the starting and ending angles.
1087 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001088 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1089 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001090 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001091 stopPoint.fY = SkScalarSin(endAngle);
1092 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001093
1094 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1095 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1096 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1097 startPoint.normalize();
1098 stopPoint.normalize();
1099
Brian Salomon3517aa72019-12-11 08:16:22 -05001100 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1101 // should orient the clip planes for arcs.
1102 SkASSERT(viewMatrix.isSimilarity());
1103 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1104 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1105 if (upperLeftDet < 0) {
1106 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001107 }
1108
Brian Salomon45c92202018-04-10 10:53:58 -04001109 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1110 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1111 SkPoint roundCaps[2];
1112 if (fRoundCaps) {
1113 // Compute the cap center points in the normalized space.
1114 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1115 roundCaps[0] = startPoint * midRadius;
1116 roundCaps[1] = stopPoint * midRadius;
1117 } else {
1118 roundCaps[0] = kUnusedRoundCaps[0];
1119 roundCaps[1] = kUnusedRoundCaps[1];
1120 }
1121
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001123 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1124 // center of the butts.
1125 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001127 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001129 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1130 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1131 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001132 if (useCenter) {
1133 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1134 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001135 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1136 if (arcParams->fSweepAngleRadians < 0) {
1137 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001138 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001139 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001140 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001141 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001142 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001143 color,
1144 innerRadius,
1145 outerRadius,
1146 {norm0.fX, norm0.fY, 0.5f},
1147 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1148 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001149 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001150 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001151 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001152 fClipPlaneIsect = false;
1153 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001155 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001156 color,
1157 innerRadius,
1158 outerRadius,
1159 {norm0.fX, norm0.fY, 0.5f},
1160 {norm1.fX, norm1.fY, 0.5f},
1161 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001162 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001163 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001164 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001165 fClipPlaneIsect = true;
1166 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001167 }
1168 } else {
1169 // We clip to a secant of the original circle.
1170 startPoint.scale(radius);
1171 stopPoint.scale(radius);
1172 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1173 norm.normalize();
1174 if (arcParams->fSweepAngleRadians > 0) {
1175 norm.negate();
1176 }
1177 SkScalar d = -norm.dot(startPoint) + 0.5f;
1178
Brian Salomon05441c42017-05-15 16:45:49 -04001179 fCircles.emplace_back(
1180 Circle{color,
1181 innerRadius,
1182 outerRadius,
1183 {norm.fX, norm.fY, d},
1184 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1185 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001186 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001187 devBounds,
1188 stroked});
1189 fClipPlane = true;
1190 fClipPlaneIsect = false;
1191 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001192 }
1193 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001194 fCircles.emplace_back(
1195 Circle{color,
1196 innerRadius,
1197 outerRadius,
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1200 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001201 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001202 devBounds,
1203 stroked});
1204 fClipPlane = false;
1205 fClipPlaneIsect = false;
1206 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001207 }
bsalomon88cf17d2016-07-08 06:40:56 -07001208 // Use the original radius and stroke radius for the bounds so that it does not include the
1209 // AA bloat.
1210 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001211 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001212 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001213 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001214 fVertCount = circle_type_to_vert_count(stroked);
1215 fIndexCount = circle_type_to_index_count(stroked);
1216 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001217 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001218
Brian Salomon289e3d82016-12-14 15:52:56 -05001219 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001220
Chris Dalton1706cbf2019-05-21 19:35:29 -06001221 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001222 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001223 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001224 } else {
1225 fHelper.visitProxies(func);
1226 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001227 }
1228
Chris Dalton6ce447a2019-06-23 18:07:38 -06001229 GrProcessorSet::Analysis finalize(
1230 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1231 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001232 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001233 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001234 GrProcessorAnalysisCoverage::kSingleChannel, color,
1235 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001236 }
1237
1238 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1239
bsalomone46f9fe2015-08-18 06:05:14 -07001240private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001241 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001242
Robert Phillips4133dc42020-03-11 15:55:55 -04001243 void onCreateProgramInfo(const GrCaps* caps,
1244 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001245 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001246 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001247 const GrXferProcessor::DstProxyView& dstProxyView,
1248 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 SkMatrix localMatrix;
1250 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001251 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001252 }
1253
Robert Phillips4490d922020-03-03 14:50:59 -05001254 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001255 fClipPlaneIsect, fClipPlaneUnion,
1256 fRoundCaps, fWideColor,
1257 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001258
Brian Salomon8afde5f2020-04-01 16:22:00 -04001259 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001260 dstProxyView, gp, GrPrimitiveType::kTriangles,
1261 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001262 }
1263
Robert Phillips4490d922020-03-03 14:50:59 -05001264 void onPrepareDraws(Target* target) override {
1265 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001266 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001267 if (!fProgramInfo) {
1268 return;
1269 }
1270 }
1271
Brian Salomon12d22642019-01-29 14:38:50 -05001272 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001273 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001274 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1275 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001276 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001277 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001278 return;
1279 }
1280
Brian Salomon12d22642019-01-29 14:38:50 -05001281 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001282 int firstIndex = 0;
1283 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1284 if (!indices) {
1285 SkDebugf("Could not allocate indices\n");
1286 return;
1287 }
1288
1289 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001290 for (const auto& circle : fCircles) {
1291 SkScalar innerRadius = circle.fInnerRadius;
1292 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001293 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001294 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001295
joshualitt76e7fb62015-02-11 08:52:27 -08001296 // The inner radius in the vertex data must be specified in normalized space.
1297 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001298 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001299
1300 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001301 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001302
Brian Osman9a24fee2018-08-03 09:48:42 -04001303 SkVector geoClipPlane = { 0, 0 };
1304 SkScalar offsetClipDist = SK_Scalar1;
1305 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1306 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1307 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1308 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1309 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1310 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1311 // the AA can extend just past the center of the circle.
1312 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1313 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1314 SkAssertResult(geoClipPlane.normalize());
1315 offsetClipDist = 0.5f / halfWidth;
1316 }
1317
Brian Osman7d8f82b2018-11-08 10:24:09 -05001318 for (int i = 0; i < 8; ++i) {
1319 // This clips the normalized offset to the half-plane we computed above. Then we
1320 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001321 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001322 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001323 vertices.write(center + offset * halfWidth,
1324 color,
1325 offset,
1326 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001327 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001328 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001329 }
1330 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001331 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001332 }
1333 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001334 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001335 }
1336 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001337 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001338 }
Brian Salomon45c92202018-04-10 10:53:58 -04001339 }
jvanverth6ca48822016-10-07 06:57:32 -07001340
Brian Salomon05441c42017-05-15 16:45:49 -04001341 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001342 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001343
Brian Osman7d8f82b2018-11-08 10:24:09 -05001344 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001345 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1346 color,
1347 kOctagonInner[i] * innerRadius,
1348 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001349 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001350 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001351 }
1352 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001353 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001354 }
1355 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 }
1358 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001359 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001360 }
Brian Salomon45c92202018-04-10 10:53:58 -04001361 }
jvanverth6ca48822016-10-07 06:57:32 -07001362 } else {
1363 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001365 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001366 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001367 }
jvanverth6ca48822016-10-07 06:57:32 -07001368 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001370 }
1371 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001372 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001373 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001374 if (fRoundCaps) {
1375 vertices.write(circle.fRoundCapCenters);
1376 }
jvanverth6ca48822016-10-07 06:57:32 -07001377 }
1378
Brian Salomon05441c42017-05-15 16:45:49 -04001379 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1380 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001381 for (int i = 0; i < primIndexCount; ++i) {
1382 *indices++ = primIndices[i] + currStartVertex;
1383 }
1384
Brian Salomon05441c42017-05-15 16:45:49 -04001385 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001386 }
jvanverth6ca48822016-10-07 06:57:32 -07001387
Robert Phillips4490d922020-03-03 14:50:59 -05001388 fMesh = target->allocMesh();
1389 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001390 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001391 }
1392
1393 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001394 if (!fProgramInfo || !fMesh) {
1395 return;
1396 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001397
Chris Dalton765ed362020-03-16 17:34:44 -06001398 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1399 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1400 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001401 }
1402
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001403 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1404 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001405 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001406
1407 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001408 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001409 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001410 }
1411
Brian Salomon05441c42017-05-15 16:45:49 -04001412 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001413 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001414 }
1415
Brian Salomon05441c42017-05-15 16:45:49 -04001416 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001417 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1418 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001419 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001420 }
1421
Brian Salomon289e3d82016-12-14 15:52:56 -05001422 // Because we've set up the ops that don't use the planes with noop values
1423 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001424 fClipPlane |= that->fClipPlane;
1425 fClipPlaneIsect |= that->fClipPlaneIsect;
1426 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001427 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001428 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001429
Brian Salomon05441c42017-05-15 16:45:49 -04001430 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001431 fVertCount += that->fVertCount;
1432 fIndexCount += that->fIndexCount;
1433 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001434 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001435 }
1436
John Stilesaf366522020-08-13 09:57:34 -04001437#if GR_TEST_UTILS
1438 SkString onDumpInfo() const override {
1439 SkString string;
1440 for (int i = 0; i < fCircles.count(); ++i) {
1441 string.appendf(
1442 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1443 "InnerRad: %.2f, OuterRad: %.2f\n",
1444 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1445 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1446 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1447 fCircles[i].fOuterRadius);
1448 }
1449 string += fHelper.dumpInfo();
1450 return string;
1451 }
1452#endif
1453
Brian Salomon05441c42017-05-15 16:45:49 -04001454 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001455 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001456 SkScalar fInnerRadius;
1457 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001458 SkScalar fClipPlane[3];
1459 SkScalar fIsectPlane[3];
1460 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001461 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001462 SkRect fDevBounds;
1463 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001464 };
1465
Brian Salomon289e3d82016-12-14 15:52:56 -05001466 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001467 Helper fHelper;
1468 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001469 int fVertCount;
1470 int fIndexCount;
1471 bool fAllFill;
1472 bool fClipPlane;
1473 bool fClipPlaneIsect;
1474 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001475 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001476 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001477
Chris Daltoneb694b72020-03-16 09:25:50 -06001478 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001479 GrProgramInfo* fProgramInfo = nullptr;
1480
John Stiles7571f9e2020-09-02 22:42:33 -04001481 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001482};
1483
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001484class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1485private:
1486 using Helper = GrSimpleMeshDrawOpHelper;
1487
1488public:
1489 DEFINE_OP_CLASS_ID
1490
Herb Derbyc76d4092020-10-07 16:46:15 -04001491 static GrOp::Owner Make(GrRecordingContext* context,
1492 GrPaint&& paint,
1493 const SkMatrix& viewMatrix,
1494 SkPoint center,
1495 SkScalar radius,
1496 SkScalar strokeWidth,
1497 SkScalar startAngle,
1498 SkScalar onAngle,
1499 SkScalar offAngle,
1500 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001501 SkASSERT(circle_stays_circle(viewMatrix));
1502 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001503 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1504 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001505 onAngle, offAngle, phaseAngle);
1506 }
1507
Herb Derbyc76d4092020-10-07 16:46:15 -04001508 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001509 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1510 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1511 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001512 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001513 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001514 SkASSERT(circle_stays_circle(viewMatrix));
1515 viewMatrix.mapPoints(&center, 1);
1516 radius = viewMatrix.mapRadius(radius);
1517 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1518
1519 // Determine the angle where the circle starts in device space and whether its orientation
1520 // has been reversed.
1521 SkVector start;
1522 bool reflection;
1523 if (!startAngle) {
1524 start = {1, 0};
1525 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001526 start.fY = SkScalarSin(startAngle);
1527 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001528 }
1529 viewMatrix.mapVectors(&start, 1);
1530 startAngle = SkScalarATan2(start.fY, start.fX);
1531 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1532 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1533
1534 auto totalAngle = onAngle + offAngle;
1535 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1536
1537 SkScalar halfWidth = 0;
1538 if (SkScalarNearlyZero(strokeWidth)) {
1539 halfWidth = SK_ScalarHalf;
1540 } else {
1541 halfWidth = SkScalarHalf(strokeWidth);
1542 }
1543
1544 SkScalar outerRadius = radius + halfWidth;
1545 SkScalar innerRadius = radius - halfWidth;
1546
1547 // The radii are outset for two reasons. First, it allows the shader to simply perform
1548 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1549 // Second, the outer radius is used to compute the verts of the bounding box that is
1550 // rendered and the outset ensures the box will cover all partially covered by the circle.
1551 outerRadius += SK_ScalarHalf;
1552 innerRadius -= SK_ScalarHalf;
1553 fViewMatrixIfUsingLocalCoords = viewMatrix;
1554
1555 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1556 center.fX + outerRadius, center.fY + outerRadius);
1557
1558 // We store whether there is a reflection as a negative total angle.
1559 if (reflection) {
1560 totalAngle = -totalAngle;
1561 }
1562 fCircles.push_back(Circle{
1563 color,
1564 outerRadius,
1565 innerRadius,
1566 onAngle,
1567 totalAngle,
1568 startAngle,
1569 phaseAngle,
1570 devBounds
1571 });
1572 // Use the original radius and stroke radius for the bounds so that it does not include the
1573 // AA bloat.
1574 radius += halfWidth;
1575 this->setBounds(
1576 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001577 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001578 fVertCount = circle_type_to_vert_count(true);
1579 fIndexCount = circle_type_to_index_count(true);
1580 }
1581
1582 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1583
Chris Dalton1706cbf2019-05-21 19:35:29 -06001584 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001585 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001586 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001587 } else {
1588 fHelper.visitProxies(func);
1589 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001590 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001591
Chris Dalton6ce447a2019-06-23 18:07:38 -06001592 GrProcessorSet::Analysis finalize(
1593 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1594 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001595 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001596 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001597 GrProcessorAnalysisCoverage::kSingleChannel, color,
1598 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001599 }
1600
1601 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1602
1603private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001604 GrProgramInfo* programInfo() override { return fProgramInfo; }
1605
Robert Phillips4133dc42020-03-11 15:55:55 -04001606 void onCreateProgramInfo(const GrCaps* caps,
1607 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001608 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001609 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001610 const GrXferProcessor::DstProxyView& dstProxyView,
1611 GrXferBarrierFlags renderPassXferBarriers) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001612 SkMatrix localMatrix;
1613 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001614 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001615 }
1616
1617 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001618 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001619 fWideColor,
1620 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001621
Brian Salomon8afde5f2020-04-01 16:22:00 -04001622 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001623 dstProxyView, gp, GrPrimitiveType::kTriangles,
1624 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001625 }
1626
Robert Phillips4490d922020-03-03 14:50:59 -05001627 void onPrepareDraws(Target* target) override {
1628 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001629 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001630 if (!fProgramInfo) {
1631 return;
1632 }
1633 }
1634
Brian Salomon12d22642019-01-29 14:38:50 -05001635 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001636 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001637 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1638 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001639 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001640 SkDebugf("Could not allocate vertices\n");
1641 return;
1642 }
1643
Brian Salomon12d22642019-01-29 14:38:50 -05001644 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001645 int firstIndex = 0;
1646 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1647 if (!indices) {
1648 SkDebugf("Could not allocate indices\n");
1649 return;
1650 }
1651
1652 int currStartVertex = 0;
1653 for (const auto& circle : fCircles) {
1654 // The inner radius in the vertex data must be specified in normalized space so that
1655 // length() can be called with smaller values to avoid precision issues with half
1656 // floats.
1657 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1658 const SkRect& bounds = circle.fDevBounds;
1659 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001660 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1661 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1662 };
1663 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001664 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001665 dashParams.totalAngle = -dashParams.totalAngle;
1666 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 }
1668
Brian Osmane3caf2d2018-11-21 13:48:36 -05001669 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001670
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001671 // The bounding geometry for the circle is composed of an outer bounding octagon and
1672 // an inner bounded octagon.
1673
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001674 // Compute the vertices of the outer octagon.
1675 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1676 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001677
1678 auto reflectY = [=](const SkPoint& p) {
1679 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001680 };
Brian Osman9d958b52018-11-13 12:46:56 -05001681
1682 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001683 vertices.write(center + kOctagonOuter[i] * halfWidth,
1684 color,
1685 reflectY(kOctagonOuter[i]),
1686 circle.fOuterRadius,
1687 normInnerRadius,
1688 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001689 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001690
1691 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001692 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001693 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1694 color,
1695 reflectY(kOctagonInner[i]) * normInnerRadius,
1696 circle.fOuterRadius,
1697 normInnerRadius,
1698 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001699 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700
1701 const uint16_t* primIndices = circle_type_to_indices(true);
1702 const int primIndexCount = circle_type_to_index_count(true);
1703 for (int i = 0; i < primIndexCount; ++i) {
1704 *indices++ = primIndices[i] + currStartVertex;
1705 }
1706
1707 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001708 }
1709
Robert Phillips4490d922020-03-03 14:50:59 -05001710 fMesh = target->allocMesh();
1711 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001712 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001713 }
1714
1715 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001716 if (!fProgramInfo || !fMesh) {
1717 return;
1718 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001719
Chris Dalton765ed362020-03-16 17:34:44 -06001720 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1721 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1722 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 }
1724
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001725 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1726 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001727 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1728
1729 // can only represent 65535 unique vertices with 16-bit indices
1730 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001731 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001732 }
1733
1734 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001735 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001736 }
1737
1738 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001739 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1740 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001741 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001742 }
1743
1744 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001745 fVertCount += that->fVertCount;
1746 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001747 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001748 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001749 }
1750
John Stilesaf366522020-08-13 09:57:34 -04001751#if GR_TEST_UTILS
1752 SkString onDumpInfo() const override {
1753 SkString string;
1754 for (int i = 0; i < fCircles.count(); ++i) {
1755 string.appendf(
1756 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1757 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1758 "Phase: %.2f\n",
1759 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1760 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1761 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1762 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1763 fCircles[i].fPhaseAngle);
1764 }
1765 string += fHelper.dumpInfo();
1766 return string;
1767 }
1768#endif
1769
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001770 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001771 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001772 SkScalar fOuterRadius;
1773 SkScalar fInnerRadius;
1774 SkScalar fOnAngle;
1775 SkScalar fTotalAngle;
1776 SkScalar fStartAngle;
1777 SkScalar fPhaseAngle;
1778 SkRect fDevBounds;
1779 };
1780
1781 SkMatrix fViewMatrixIfUsingLocalCoords;
1782 Helper fHelper;
1783 SkSTArray<1, Circle, true> fCircles;
1784 int fVertCount;
1785 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001786 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001787
Chris Daltoneb694b72020-03-16 09:25:50 -06001788 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001789 GrProgramInfo* fProgramInfo = nullptr;
1790
John Stiles7571f9e2020-09-02 22:42:33 -04001791 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001792};
1793
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001794///////////////////////////////////////////////////////////////////////////////
1795
Brian Salomon05441c42017-05-15 16:45:49 -04001796class EllipseOp : public GrMeshDrawOp {
1797private:
1798 using Helper = GrSimpleMeshDrawOpHelper;
1799
1800 struct DeviceSpaceParams {
1801 SkPoint fCenter;
1802 SkScalar fXRadius;
1803 SkScalar fYRadius;
1804 SkScalar fInnerXRadius;
1805 SkScalar fInnerYRadius;
1806 };
1807
joshualitt76e7fb62015-02-11 08:52:27 -08001808public:
Brian Salomon25a88092016-12-01 09:36:50 -05001809 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001810
Herb Derbyc76d4092020-10-07 16:46:15 -04001811 static GrOp::Owner Make(GrRecordingContext* context,
1812 GrPaint&& paint,
1813 const SkMatrix& viewMatrix,
1814 const SkRect& ellipse,
1815 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001816 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001817 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001818 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1819 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001820 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1821 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001822 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1823 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1824 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1825 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001826
bsalomon4b4a7cc2016-07-08 04:42:54 -07001827 // do (potentially) anisotropic mapping of stroke
1828 SkVector scaledStroke;
1829 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001830 scaledStroke.fX = SkScalarAbs(
1831 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1832 scaledStroke.fY = SkScalarAbs(
1833 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001834
1835 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001836 bool isStrokeOnly =
1837 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001838 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1839
Brian Salomon05441c42017-05-15 16:45:49 -04001840 params.fInnerXRadius = 0;
1841 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001842 if (hasStroke) {
1843 if (SkScalarNearlyZero(scaledStroke.length())) {
1844 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1845 } else {
1846 scaledStroke.scale(SK_ScalarHalf);
1847 }
1848
1849 // we only handle thick strokes for near-circular ellipses
1850 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001851 (0.5f * params.fXRadius > params.fYRadius ||
1852 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001853 return nullptr;
1854 }
1855
1856 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001857 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1858 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1859 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1860 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001861 return nullptr;
1862 }
1863
1864 // this is legit only if scale & translation (which should be the case at the moment)
1865 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001866 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1867 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001868 }
1869
Brian Salomon05441c42017-05-15 16:45:49 -04001870 params.fXRadius += scaledStroke.fX;
1871 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001872 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001873
1874 // For large ovals with low precision floats, we fall back to the path renderer.
1875 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1876 // minimum value to avoid divides by zero. With large ovals and low precision this
1877 // leads to blurring at the edge of the oval.
1878 const SkScalar kMaxOvalRadius = 16384;
1879 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1880 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1881 return nullptr;
1882 }
1883
Greg Daniel2655ede2019-04-10 00:49:28 +00001884 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001885 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001886 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001887
Herb Derbyc76d4092020-10-07 16:46:15 -04001888 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001889 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001890 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001891 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001892 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001893 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001894 SkStrokeRec::Style style = stroke.getStyle();
1895 bool isStrokeOnly =
1896 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001897
Brian Salomon05441c42017-05-15 16:45:49 -04001898 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1899 params.fInnerXRadius, params.fInnerYRadius,
1900 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1901 params.fCenter.fY - params.fYRadius,
1902 params.fCenter.fX + params.fXRadius,
1903 params.fCenter.fY + params.fYRadius)});
1904
Greg Daniel5faf4742019-10-01 15:14:44 -04001905 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001906
bsalomon4b4a7cc2016-07-08 04:42:54 -07001907 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001908 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001909
Brian Salomon05441c42017-05-15 16:45:49 -04001910 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1911 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001912 }
joshualitt76e7fb62015-02-11 08:52:27 -08001913
Brian Salomon289e3d82016-12-14 15:52:56 -05001914 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001915
Chris Dalton1706cbf2019-05-21 19:35:29 -06001916 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001917 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001918 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001919 } else {
1920 fHelper.visitProxies(func);
1921 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001922 }
1923
Chris Dalton6ce447a2019-06-23 18:07:38 -06001924 GrProcessorSet::Analysis finalize(
1925 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1926 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001927 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1928 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001929 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001930 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001931 GrProcessorAnalysisCoverage::kSingleChannel, color,
1932 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001933 }
1934
1935 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1936
bsalomone46f9fe2015-08-18 06:05:14 -07001937private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001938 GrProgramInfo* programInfo() override { return fProgramInfo; }
1939
Robert Phillips4133dc42020-03-11 15:55:55 -04001940 void onCreateProgramInfo(const GrCaps* caps,
1941 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001942 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001943 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001944 const GrXferProcessor::DstProxyView& dstProxyView,
1945 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001946 SkMatrix localMatrix;
1947 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001948 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001949 }
1950
Robert Phillips4490d922020-03-03 14:50:59 -05001951 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1952 fUseScale, localMatrix);
1953
Brian Salomon8afde5f2020-04-01 16:22:00 -04001954 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001955 dstProxyView, gp, GrPrimitiveType::kTriangles,
1956 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001957 }
1958
Robert Phillips4490d922020-03-03 14:50:59 -05001959 void onPrepareDraws(Target* target) override {
1960 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001961 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001962 if (!fProgramInfo) {
1963 return;
1964 }
1965 }
1966
1967 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001968 GrVertexWriter verts{helper.vertices()};
1969 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001970 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001971 return;
1972 }
1973
Brian Salomon05441c42017-05-15 16:45:49 -04001974 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001975 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001976 SkScalar xRadius = ellipse.fXRadius;
1977 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001978
1979 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001980 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1981 SkScalarInvert(xRadius),
1982 SkScalarInvert(yRadius),
1983 SkScalarInvert(ellipse.fInnerXRadius),
1984 SkScalarInvert(ellipse.fInnerYRadius)
1985 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001986 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1987 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001988
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001989 if (!fStroked) {
1990 // For filled ellipses we map a unit circle in the vertex attributes rather than
1991 // computing an ellipse and modifying that distance, so we normalize to 1
1992 xMaxOffset /= xRadius;
1993 yMaxOffset /= yRadius;
1994 }
1995
joshualitt76e7fb62015-02-11 08:52:27 -08001996 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001997 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1998 color,
1999 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002000 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002001 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002002 }
Robert Phillips4490d922020-03-03 14:50:59 -05002003 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002004 }
2005
2006 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002007 if (!fProgramInfo || !fMesh) {
2008 return;
2009 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002010
Chris Dalton765ed362020-03-16 17:34:44 -06002011 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2012 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2013 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002014 }
2015
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002016 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2017 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002018 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002019
Brian Salomon05441c42017-05-15 16:45:49 -04002020 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002021 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002022 }
2023
bsalomoncdaa97b2016-03-08 08:30:14 -08002024 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002025 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002026 }
2027
Brian Salomon05441c42017-05-15 16:45:49 -04002028 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002029 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2030 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002031 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002032 }
2033
Brian Salomon05441c42017-05-15 16:45:49 -04002034 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002035 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002036 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002037 }
2038
John Stilesaf366522020-08-13 09:57:34 -04002039#if GR_TEST_UTILS
2040 SkString onDumpInfo() const override {
2041 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2042 for (const auto& geo : fEllipses) {
2043 string.appendf(
2044 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2045 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2046 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2047 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2048 geo.fInnerXRadius, geo.fInnerYRadius);
2049 }
2050 string += fHelper.dumpInfo();
2051 return string;
2052 }
2053#endif
2054
Brian Salomon05441c42017-05-15 16:45:49 -04002055 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002056 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002057 SkScalar fXRadius;
2058 SkScalar fYRadius;
2059 SkScalar fInnerXRadius;
2060 SkScalar fInnerYRadius;
2061 SkRect fDevBounds;
2062 };
joshualitt76e7fb62015-02-11 08:52:27 -08002063
Brian Salomon289e3d82016-12-14 15:52:56 -05002064 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002065 Helper fHelper;
2066 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002067 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002068 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002069 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002070
Chris Daltoneb694b72020-03-16 09:25:50 -06002071 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002072 GrProgramInfo* fProgramInfo = nullptr;
2073
John Stiles7571f9e2020-09-02 22:42:33 -04002074 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002075};
2076
joshualitt76e7fb62015-02-11 08:52:27 -08002077/////////////////////////////////////////////////////////////////////////////////////////////////
2078
Brian Salomon05441c42017-05-15 16:45:49 -04002079class DIEllipseOp : public GrMeshDrawOp {
2080private:
2081 using Helper = GrSimpleMeshDrawOpHelper;
2082
2083 struct DeviceSpaceParams {
2084 SkPoint fCenter;
2085 SkScalar fXRadius;
2086 SkScalar fYRadius;
2087 SkScalar fInnerXRadius;
2088 SkScalar fInnerYRadius;
2089 DIEllipseStyle fStyle;
2090 };
2091
joshualitt76e7fb62015-02-11 08:52:27 -08002092public:
Brian Salomon25a88092016-12-01 09:36:50 -05002093 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002094
Herb Derbyc76d4092020-10-07 16:46:15 -04002095 static GrOp::Owner Make(GrRecordingContext* context,
2096 GrPaint&& paint,
2097 const SkMatrix& viewMatrix,
2098 const SkRect& ellipse,
2099 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002100 DeviceSpaceParams params;
2101 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2102 params.fXRadius = SkScalarHalf(ellipse.width());
2103 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002104
bsalomon4b4a7cc2016-07-08 04:42:54 -07002105 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002106 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2107 ? DIEllipseStyle::kStroke
2108 : (SkStrokeRec::kHairline_Style == style)
2109 ? DIEllipseStyle::kHairline
2110 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002111
Brian Salomon05441c42017-05-15 16:45:49 -04002112 params.fInnerXRadius = 0;
2113 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002114 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2115 SkScalar strokeWidth = stroke.getWidth();
2116
2117 if (SkScalarNearlyZero(strokeWidth)) {
2118 strokeWidth = SK_ScalarHalf;
2119 } else {
2120 strokeWidth *= SK_ScalarHalf;
2121 }
2122
2123 // we only handle thick strokes for near-circular ellipses
2124 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002125 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2126 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002127 return nullptr;
2128 }
2129
2130 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002131 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2132 (strokeWidth * strokeWidth) * params.fXRadius) {
2133 return nullptr;
2134 }
2135 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2136 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002137 return nullptr;
2138 }
2139
2140 // set inner radius (if needed)
2141 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002142 params.fInnerXRadius = params.fXRadius - strokeWidth;
2143 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002144 }
2145
Brian Salomon05441c42017-05-15 16:45:49 -04002146 params.fXRadius += strokeWidth;
2147 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002148 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002149
2150 // For large ovals with low precision floats, we fall back to the path renderer.
2151 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2152 // minimum value to avoid divides by zero. With large ovals and low precision this
2153 // leads to blurring at the edge of the oval.
2154 const SkScalar kMaxOvalRadius = 16384;
2155 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2156 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2157 return nullptr;
2158 }
2159
Brian Salomon05441c42017-05-15 16:45:49 -04002160 if (DIEllipseStyle::kStroke == params.fStyle &&
2161 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2162 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002163 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002164 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002165 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002166
Herb Derbyc76d4092020-10-07 16:46:15 -04002167 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002168 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002169 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002170 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002171 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002172 // This expands the outer rect so that after CTM we end up with a half-pixel border
2173 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2174 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2175 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2176 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2177 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2178 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002179
Brian Salomon05441c42017-05-15 16:45:49 -04002180 fEllipses.emplace_back(
2181 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2182 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2183 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2184 params.fCenter.fY - params.fYRadius - geoDy,
2185 params.fCenter.fX + params.fXRadius + geoDx,
2186 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002187 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002188 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002189 }
2190
Brian Salomon289e3d82016-12-14 15:52:56 -05002191 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002192
Chris Dalton1706cbf2019-05-21 19:35:29 -06002193 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002194 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002195 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002196 } else {
2197 fHelper.visitProxies(func);
2198 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002199 }
2200
Chris Dalton6ce447a2019-06-23 18:07:38 -06002201 GrProcessorSet::Analysis finalize(
2202 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2203 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002204 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2205 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002206 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002207 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002208 GrProcessorAnalysisCoverage::kSingleChannel, color,
2209 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002210 }
2211
2212 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2213
bsalomone46f9fe2015-08-18 06:05:14 -07002214private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002215 GrProgramInfo* programInfo() override { return fProgramInfo; }
2216
Robert Phillips4133dc42020-03-11 15:55:55 -04002217 void onCreateProgramInfo(const GrCaps* caps,
2218 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002219 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002220 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002221 const GrXferProcessor::DstProxyView& dstProxyView,
2222 GrXferBarrierFlags renderPassXferBarriers) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002223 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2224 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002225 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002226
Brian Salomon8afde5f2020-04-01 16:22:00 -04002227 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002228 dstProxyView, gp, GrPrimitiveType::kTriangles,
2229 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002230 }
2231
Robert Phillips4490d922020-03-03 14:50:59 -05002232 void onPrepareDraws(Target* target) override {
2233 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002234 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002235 }
2236
2237 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002238 GrVertexWriter verts{helper.vertices()};
2239 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002240 return;
2241 }
2242
Brian Salomon05441c42017-05-15 16:45:49 -04002243 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002244 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002245 SkScalar xRadius = ellipse.fXRadius;
2246 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002247
joshualitt76e7fb62015-02-11 08:52:27 -08002248 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002249 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2250 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002251
Brian Osman9d958b52018-11-13 12:46:56 -05002252 // By default, constructed so that inner offset is (0, 0) for all points
2253 SkScalar innerRatioX = -offsetDx;
2254 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002255
Brian Osman9d958b52018-11-13 12:46:56 -05002256 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002257 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002258 innerRatioX = xRadius / ellipse.fInnerXRadius;
2259 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002260 }
joshualitt76e7fb62015-02-11 08:52:27 -08002261
Brian Osman2b6e3902018-11-21 15:29:43 -05002262 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2263 color,
2264 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002265 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002266 origin_centered_tri_strip(innerRatioX + offsetDx,
2267 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002268 }
Robert Phillips4490d922020-03-03 14:50:59 -05002269 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002270 }
2271
2272 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002273 if (!fProgramInfo || !fMesh) {
2274 return;
2275 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002276
Chris Dalton765ed362020-03-16 17:34:44 -06002277 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2278 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2279 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002280 }
halcanary9d524f22016-03-29 09:03:52 -07002281
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002282 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2283 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002284 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002285 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002286 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002287 }
2288
bsalomoncdaa97b2016-03-08 08:30:14 -08002289 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002290 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002291 }
2292
joshualittd96a67b2015-05-05 14:09:05 -07002293 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002294 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002295 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002296 }
2297
Brian Salomon05441c42017-05-15 16:45:49 -04002298 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002299 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002300 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002301 }
2302
John Stilesaf366522020-08-13 09:57:34 -04002303#if GR_TEST_UTILS
2304 SkString onDumpInfo() const override {
2305 SkString string;
2306 for (const auto& geo : fEllipses) {
2307 string.appendf(
2308 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2309 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2310 "GeoDY: %.2f\n",
2311 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2312 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2313 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2314 }
2315 string += fHelper.dumpInfo();
2316 return string;
2317 }
2318#endif
2319
Brian Salomon05441c42017-05-15 16:45:49 -04002320 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2321 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002322
Brian Salomon05441c42017-05-15 16:45:49 -04002323 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002324 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002325 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002326 SkScalar fXRadius;
2327 SkScalar fYRadius;
2328 SkScalar fInnerXRadius;
2329 SkScalar fInnerYRadius;
2330 SkScalar fGeoDx;
2331 SkScalar fGeoDy;
2332 DIEllipseStyle fStyle;
2333 SkRect fBounds;
2334 };
2335
Brian Salomon05441c42017-05-15 16:45:49 -04002336 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002337 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002338 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002339 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002340
Chris Daltoneb694b72020-03-16 09:25:50 -06002341 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002342 GrProgramInfo* fProgramInfo = nullptr;
2343
John Stiles7571f9e2020-09-02 22:42:33 -04002344 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002345};
2346
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002347///////////////////////////////////////////////////////////////////////////////
2348
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002349// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002350//
2351// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2352// ____________
2353// |_|________|_|
2354// | | | |
2355// | | | |
2356// | | | |
2357// |_|________|_|
2358// |_|________|_|
2359//
2360// For strokes, we don't draw the center quad.
2361//
2362// For circular roundrects, in the case where the stroke width is greater than twice
2363// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002364// in the center. The shared vertices are duplicated so we can set a different outer radius
2365// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002366// ____________
2367// |_|________|_|
2368// | |\ ____ /| |
2369// | | | | | |
2370// | | |____| | |
2371// |_|/______\|_|
2372// |_|________|_|
2373//
2374// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002375//
2376// For filled rrects that need to provide a distance vector we resuse the overstroke
2377// geometry but make the inner rect degenerate (either a point or a horizontal or
2378// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002379
jvanverth84839f62016-08-29 10:16:40 -07002380static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002381 // clang-format off
2382 // overstroke quads
2383 // we place this at the beginning so that we can skip these indices when rendering normally
2384 16, 17, 19, 16, 19, 18,
2385 19, 17, 23, 19, 23, 21,
2386 21, 23, 22, 21, 22, 20,
2387 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002388
Brian Salomon289e3d82016-12-14 15:52:56 -05002389 // corners
2390 0, 1, 5, 0, 5, 4,
2391 2, 3, 7, 2, 7, 6,
2392 8, 9, 13, 8, 13, 12,
2393 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002394
Brian Salomon289e3d82016-12-14 15:52:56 -05002395 // edges
2396 1, 2, 6, 1, 6, 5,
2397 4, 5, 9, 4, 9, 8,
2398 6, 7, 11, 6, 11, 10,
2399 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002400
Brian Salomon289e3d82016-12-14 15:52:56 -05002401 // center
2402 // we place this at the end so that we can ignore these indices when not rendering as filled
2403 5, 6, 10, 5, 10, 9,
2404 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002405};
Brian Salomon289e3d82016-12-14 15:52:56 -05002406
jvanverth84839f62016-08-29 10:16:40 -07002407// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002408static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409
jvanverth84839f62016-08-29 10:16:40 -07002410// overstroke count is arraysize minus the center indices
2411static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2412// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002413static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002414// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002415static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2416static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002417static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002418
jvanverthc3d0e422016-08-25 08:12:35 -07002419enum RRectType {
2420 kFill_RRectType,
2421 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002422 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002423};
2424
jvanverth84839f62016-08-29 10:16:40 -07002425static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002426 switch (type) {
2427 case kFill_RRectType:
2428 case kStroke_RRectType:
2429 return kVertsPerStandardRRect;
2430 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002431 return kVertsPerOverstrokeRRect;
2432 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002433 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002434}
2435
2436static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002437 switch (type) {
2438 case kFill_RRectType:
2439 return kIndicesPerFillRRect;
2440 case kStroke_RRectType:
2441 return kIndicesPerStrokeRRect;
2442 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002443 return kIndicesPerOverstrokeRRect;
2444 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002445 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002446}
2447
2448static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002449 switch (type) {
2450 case kFill_RRectType:
2451 case kStroke_RRectType:
2452 return gStandardRRectIndices;
2453 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002454 return gOverstrokeRRectIndices;
2455 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002456 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002457}
2458
joshualitt76e7fb62015-02-11 08:52:27 -08002459///////////////////////////////////////////////////////////////////////////////////////////////////
2460
Robert Phillips79839d42016-10-06 15:03:34 -04002461// For distance computations in the interior of filled rrects we:
2462//
2463// add a interior degenerate (point or line) rect
2464// each vertex of that rect gets -outerRad as its radius
2465// this makes the computation of the distance to the outer edge be negative
2466// negative values are caught and then handled differently in the GP's onEmitCode
2467// each vertex is also given the normalized x & y distance from the interior rect's edge
2468// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2469
Brian Salomon05441c42017-05-15 16:45:49 -04002470class CircularRRectOp : public GrMeshDrawOp {
2471private:
2472 using Helper = GrSimpleMeshDrawOpHelper;
2473
joshualitt76e7fb62015-02-11 08:52:27 -08002474public:
Brian Salomon25a88092016-12-01 09:36:50 -05002475 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002476
bsalomon4b4a7cc2016-07-08 04:42:54 -07002477 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2478 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002479 static GrOp::Owner Make(GrRecordingContext* context,
2480 GrPaint&& paint,
2481 const SkMatrix& viewMatrix,
2482 const SkRect& devRect,
2483 float devRadius,
2484 float devStrokeWidth,
2485 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002486 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002487 devRect, devRadius,
2488 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002489 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002490 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002491 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2492 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002493 : INHERITED(ClassID())
2494 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002495 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002496 SkRect bounds = devRect;
2497 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2498 SkScalar innerRadius = 0.0f;
2499 SkScalar outerRadius = devRadius;
2500 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002501 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002502 if (devStrokeWidth > 0) {
2503 if (SkScalarNearlyZero(devStrokeWidth)) {
2504 halfWidth = SK_ScalarHalf;
2505 } else {
2506 halfWidth = SkScalarHalf(devStrokeWidth);
2507 }
joshualitt76e7fb62015-02-11 08:52:27 -08002508
bsalomon4b4a7cc2016-07-08 04:42:54 -07002509 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002510 // Outset stroke by 1/4 pixel
2511 devStrokeWidth += 0.25f;
2512 // If stroke is greater than width or height, this is still a fill
2513 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002514 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002515 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002516 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002517 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002518 }
2519 outerRadius += halfWidth;
2520 bounds.outset(halfWidth, halfWidth);
2521 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002522
Greg Daniel2655ede2019-04-10 00:49:28 +00002523 // The radii are outset for two reasons. First, it allows the shader to simply perform
2524 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2525 // Second, the outer radius is used to compute the verts of the bounding box that is
2526 // rendered and the outset ensures the box will cover all partially covered by the rrect
2527 // corners.
2528 outerRadius += SK_ScalarHalf;
2529 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002530
Greg Daniel5faf4742019-10-01 15:14:44 -04002531 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002532
Greg Daniel2655ede2019-04-10 00:49:28 +00002533 // Expand the rect for aa to generate correct vertices.
2534 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002535
Brian Salomon05441c42017-05-15 16:45:49 -04002536 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002537 fVertCount = rrect_type_to_vert_count(type);
2538 fIndexCount = rrect_type_to_index_count(type);
2539 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002540 }
2541
Brian Salomon289e3d82016-12-14 15:52:56 -05002542 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002543
Chris Dalton1706cbf2019-05-21 19:35:29 -06002544 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002545 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002546 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002547 } else {
2548 fHelper.visitProxies(func);
2549 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002550 }
2551
Chris Dalton6ce447a2019-06-23 18:07:38 -06002552 GrProcessorSet::Analysis finalize(
2553 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2554 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002555 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002556 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002557 GrProcessorAnalysisCoverage::kSingleChannel, color,
2558 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002559 }
2560
2561 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2562
Brian Salomon92aee3d2016-12-21 09:20:25 -05002563private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002564 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002565 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002566 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002567 SkASSERT(smInset < bigInset);
2568
2569 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002570 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2571 color,
2572 xOffset, 0.0f,
2573 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002574
2575 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002576 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2577 color,
2578 xOffset, 0.0f,
2579 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002580
Brian Osmana1d4eb92018-12-06 16:33:10 -05002581 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2582 color,
2583 0.0f, 0.0f,
2584 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002585
Brian Osmana1d4eb92018-12-06 16:33:10 -05002586 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2587 color,
2588 0.0f, 0.0f,
2589 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002590
Brian Osmana1d4eb92018-12-06 16:33:10 -05002591 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2592 color,
2593 0.0f, 0.0f,
2594 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002595
Brian Osmana1d4eb92018-12-06 16:33:10 -05002596 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2597 color,
2598 0.0f, 0.0f,
2599 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002600
2601 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002602 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2603 color,
2604 xOffset, 0.0f,
2605 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002606
2607 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002608 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2609 color,
2610 xOffset, 0.0f,
2611 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002612 }
2613
Robert Phillips2669a7b2020-03-12 12:07:19 -04002614 GrProgramInfo* programInfo() override { return fProgramInfo; }
2615
Robert Phillips4133dc42020-03-11 15:55:55 -04002616 void onCreateProgramInfo(const GrCaps* caps,
2617 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002618 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002619 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002620 const GrXferProcessor::DstProxyView& dstProxyView,
2621 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002622 // Invert the view matrix as a local matrix (if any other processors require coords).
2623 SkMatrix localMatrix;
2624 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002625 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002626 }
2627
Robert Phillips4490d922020-03-03 14:50:59 -05002628 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002629 false, false, false, false,
2630 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002631
Brian Salomon8afde5f2020-04-01 16:22:00 -04002632 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002633 dstProxyView, gp, GrPrimitiveType::kTriangles,
2634 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002635 }
2636
Robert Phillips4490d922020-03-03 14:50:59 -05002637 void onPrepareDraws(Target* target) override {
2638 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002639 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002640 if (!fProgramInfo) {
2641 return;
2642 }
2643 }
2644
Brian Salomon12d22642019-01-29 14:38:50 -05002645 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002646 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002647
Robert Phillips4490d922020-03-03 14:50:59 -05002648 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2649 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002650 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002651 SkDebugf("Could not allocate vertices\n");
2652 return;
2653 }
2654
Brian Salomon12d22642019-01-29 14:38:50 -05002655 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002656 int firstIndex = 0;
2657 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2658 if (!indices) {
2659 SkDebugf("Could not allocate indices\n");
2660 return;
2661 }
2662
2663 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002664 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002665 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002666 SkScalar outerRadius = rrect.fOuterRadius;
2667 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002668
Brian Salomon289e3d82016-12-14 15:52:56 -05002669 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2670 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002671
Brian Salomon289e3d82016-12-14 15:52:56 -05002672 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002673 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002674 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002675 SkScalar innerRadius = rrect.fType != kFill_RRectType
2676 ? rrect.fInnerRadius / rrect.fOuterRadius
2677 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002678 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002679 verts.write(bounds.fLeft, yCoords[i],
2680 color,
2681 -1.0f, yOuterRadii[i],
2682 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002683
Brian Osmana1d4eb92018-12-06 16:33:10 -05002684 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2685 color,
2686 0.0f, yOuterRadii[i],
2687 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002688
Brian Osmana1d4eb92018-12-06 16:33:10 -05002689 verts.write(bounds.fRight - outerRadius, yCoords[i],
2690 color,
2691 0.0f, yOuterRadii[i],
2692 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002693
Brian Osmana1d4eb92018-12-06 16:33:10 -05002694 verts.write(bounds.fRight, yCoords[i],
2695 color,
2696 1.0f, yOuterRadii[i],
2697 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002698 }
jvanverthc3d0e422016-08-25 08:12:35 -07002699 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002700 // Effectively this is an additional stroked rrect, with its
2701 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2702 // This will give us correct AA in the center and the correct
2703 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002704 //
jvanvertha4f1af82016-08-29 07:17:47 -07002705 // Also, the outer offset is a constant vector pointing to the right, which
2706 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002707 if (kOverstroke_RRectType == rrect.fType) {
2708 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002709
Brian Salomon05441c42017-05-15 16:45:49 -04002710 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002711 // this is the normalized distance from the outer rectangle of this
2712 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002713 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002714
Brian Osmana1d4eb92018-12-06 16:33:10 -05002715 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002716 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002717 }
jvanverth6a397612016-08-26 08:15:33 -07002718
Brian Salomon05441c42017-05-15 16:45:49 -04002719 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2720 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002721 for (int i = 0; i < primIndexCount; ++i) {
2722 *indices++ = primIndices[i] + currStartVertex;
2723 }
2724
Brian Salomon05441c42017-05-15 16:45:49 -04002725 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002726 }
2727
Robert Phillips4490d922020-03-03 14:50:59 -05002728 fMesh = target->allocMesh();
2729 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002730 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002731 }
2732
2733 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002734 if (!fProgramInfo || !fMesh) {
2735 return;
2736 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002737
Chris Dalton765ed362020-03-16 17:34:44 -06002738 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2739 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2740 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002741 }
2742
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002743 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2744 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002745 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002746
2747 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002748 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002749 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002750 }
2751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002753 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002754 }
2755
Brian Salomon05441c42017-05-15 16:45:49 -04002756 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002757 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2758 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002759 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002760 }
2761
Brian Salomon05441c42017-05-15 16:45:49 -04002762 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002763 fVertCount += that->fVertCount;
2764 fIndexCount += that->fIndexCount;
2765 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002766 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002767 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002768 }
2769
John Stilesaf366522020-08-13 09:57:34 -04002770#if GR_TEST_UTILS
2771 SkString onDumpInfo() const override {
2772 SkString string;
2773 for (int i = 0; i < fRRects.count(); ++i) {
2774 string.appendf(
2775 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2776 "InnerRad: %.2f, OuterRad: %.2f\n",
2777 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2778 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2779 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2780 fRRects[i].fOuterRadius);
2781 }
2782 string += fHelper.dumpInfo();
2783 return string;
2784 }
2785#endif
2786
Brian Salomon05441c42017-05-15 16:45:49 -04002787 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002788 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002789 SkScalar fInnerRadius;
2790 SkScalar fOuterRadius;
2791 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002792 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002793 };
2794
Brian Salomon289e3d82016-12-14 15:52:56 -05002795 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002796 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002797 int fVertCount;
2798 int fIndexCount;
2799 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002800 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002801 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002802
Chris Daltoneb694b72020-03-16 09:25:50 -06002803 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002804 GrProgramInfo* fProgramInfo = nullptr;
2805
John Stiles7571f9e2020-09-02 22:42:33 -04002806 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002807};
2808
jvanverth84839f62016-08-29 10:16:40 -07002809static const int kNumRRectsInIndexBuffer = 256;
2810
2811GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2812GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002813static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2814 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002815 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2816 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2817 switch (type) {
2818 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002819 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002820 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2821 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002822 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002823 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002824 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2825 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002826 default:
2827 SkASSERT(false);
2828 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002829 }
jvanverth84839f62016-08-29 10:16:40 -07002830}
2831
Brian Salomon05441c42017-05-15 16:45:49 -04002832class EllipticalRRectOp : public GrMeshDrawOp {
2833private:
2834 using Helper = GrSimpleMeshDrawOpHelper;
2835
joshualitt76e7fb62015-02-11 08:52:27 -08002836public:
Brian Salomon25a88092016-12-01 09:36:50 -05002837 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002838
bsalomon4b4a7cc2016-07-08 04:42:54 -07002839 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2840 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002841 static GrOp::Owner Make(GrRecordingContext* context,
2842 GrPaint&& paint,
2843 const SkMatrix& viewMatrix,
2844 const SkRect& devRect,
2845 float devXRadius,
2846 float devYRadius,
2847 SkVector devStrokeWidths,
2848 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002849 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2850 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002851 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2852 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002853 if (devStrokeWidths.fX > 0) {
2854 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2855 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2856 } else {
2857 devStrokeWidths.scale(SK_ScalarHalf);
2858 }
joshualitt76e7fb62015-02-11 08:52:27 -08002859
bsalomon4b4a7cc2016-07-08 04:42:54 -07002860 // we only handle thick strokes for near-circular ellipses
2861 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002862 (SK_ScalarHalf * devXRadius > devYRadius ||
2863 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002864 return nullptr;
2865 }
2866
2867 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002868 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2869 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002870 return nullptr;
2871 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002872 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2873 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002874 return nullptr;
2875 }
Brian Salomon05441c42017-05-15 16:45:49 -04002876 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002877 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002878 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002879 devXRadius, devYRadius, devStrokeWidths,
2880 strokeOnly);
2881 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002882
Herb Derbyc76d4092020-10-07 16:46:15 -04002883 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002884 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2885 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002886 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002887 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002888 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002889 SkScalar innerXRadius = 0.0f;
2890 SkScalar innerYRadius = 0.0f;
2891 SkRect bounds = devRect;
2892 bool stroked = false;
2893 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002894 // this is legit only if scale & translation (which should be the case at the moment)
2895 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002896 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2897 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002898 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2899 }
2900
Brian Salomon05441c42017-05-15 16:45:49 -04002901 devXRadius += devStrokeHalfWidths.fX;
2902 devYRadius += devStrokeHalfWidths.fY;
2903 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002904 }
2905
Brian Salomon05441c42017-05-15 16:45:49 -04002906 fStroked = stroked;
2907 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002908 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002909 // Expand the rect for aa in order to generate the correct vertices.
2910 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002911 fRRects.emplace_back(
2912 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002913 }
2914
Brian Salomon289e3d82016-12-14 15:52:56 -05002915 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002916
Chris Dalton1706cbf2019-05-21 19:35:29 -06002917 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002918 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002919 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002920 } else {
2921 fHelper.visitProxies(func);
2922 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002923 }
2924
Chris Dalton6ce447a2019-06-23 18:07:38 -06002925 GrProcessorSet::Analysis finalize(
2926 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2927 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002928 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002929 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002930 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002931 GrProcessorAnalysisCoverage::kSingleChannel, color,
2932 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002933 }
2934
2935 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2936
bsalomone46f9fe2015-08-18 06:05:14 -07002937private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002938 GrProgramInfo* programInfo() override { return fProgramInfo; }
2939
Robert Phillips4133dc42020-03-11 15:55:55 -04002940 void onCreateProgramInfo(const GrCaps* caps,
2941 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002942 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002943 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002944 const GrXferProcessor::DstProxyView& dstProxyView,
2945 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002946 SkMatrix localMatrix;
2947 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002948 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002949 }
2950
Robert Phillips4490d922020-03-03 14:50:59 -05002951 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2952 fUseScale, localMatrix);
2953
Brian Salomon8afde5f2020-04-01 16:22:00 -04002954 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002955 dstProxyView, gp, GrPrimitiveType::kTriangles,
2956 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002957 }
2958
Robert Phillips4490d922020-03-03 14:50:59 -05002959 void onPrepareDraws(Target* target) override {
2960 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002961 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002962 if (!fProgramInfo) {
2963 return;
2964 }
2965 }
joshualitt76e7fb62015-02-11 08:52:27 -08002966
bsalomonb5238a72015-05-05 07:49:49 -07002967 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002968 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002969 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2970 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002971
Brian Salomon12d22642019-01-29 14:38:50 -05002972 if (!indexBuffer) {
2973 SkDebugf("Could not allocate indices\n");
2974 return;
2975 }
Robert Phillips4490d922020-03-03 14:50:59 -05002976 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2977 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002978 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002979 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002980 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002981 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002982 SkDebugf("Could not allocate vertices\n");
2983 return;
2984 }
2985
Brian Salomon05441c42017-05-15 16:45:49 -04002986 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002987 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002988 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002989 float reciprocalRadii[4] = {
2990 SkScalarInvert(rrect.fXRadius),
2991 SkScalarInvert(rrect.fYRadius),
2992 SkScalarInvert(rrect.fInnerXRadius),
2993 SkScalarInvert(rrect.fInnerYRadius)
2994 };
joshualitt76e7fb62015-02-11 08:52:27 -08002995
2996 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00002997 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2998 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002999
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003000 SkScalar xMaxOffset = xOuterRadius;
3001 SkScalar yMaxOffset = yOuterRadius;
3002 if (!fStroked) {
3003 // For filled rrects we map a unit circle in the vertex attributes rather than
3004 // computing an ellipse and modifying that distance, so we normalize to 1.
3005 xMaxOffset /= rrect.fXRadius;
3006 yMaxOffset /= rrect.fYRadius;
3007 }
3008
Brian Salomon05441c42017-05-15 16:45:49 -04003009 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003010
Brian Salomon289e3d82016-12-14 15:52:56 -05003011 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3012 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003013 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003014 SK_ScalarNearlyZero, // we're using inversesqrt() in
3015 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003016 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003017
Brian Osman788b9162020-02-07 10:36:46 -05003018 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003019 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003020 verts.write(bounds.fLeft, yCoords[i],
3021 color,
3022 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003023 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003024 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003025
Brian Osmana1d4eb92018-12-06 16:33:10 -05003026 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3027 color,
3028 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003029 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003030 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003031
Brian Osmana1d4eb92018-12-06 16:33:10 -05003032 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3033 color,
3034 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003035 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003036 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003037
Brian Osmana1d4eb92018-12-06 16:33:10 -05003038 verts.write(bounds.fRight, yCoords[i],
3039 color,
3040 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003041 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003042 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003043 }
3044 }
Robert Phillips4490d922020-03-03 14:50:59 -05003045 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003046 }
3047
3048 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003049 if (!fProgramInfo || !fMesh) {
3050 return;
3051 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003052
Chris Dalton765ed362020-03-16 17:34:44 -06003053 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3054 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3055 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003056 }
3057
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003058 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3059 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003060 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003061
Brian Salomon05441c42017-05-15 16:45:49 -04003062 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003063 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003064 }
3065
bsalomoncdaa97b2016-03-08 08:30:14 -08003066 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003067 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003068 }
3069
Brian Salomon05441c42017-05-15 16:45:49 -04003070 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003071 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3072 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003073 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003074 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003075
Brian Salomon05441c42017-05-15 16:45:49 -04003076 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003077 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003078 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003079 }
3080
John Stilesaf366522020-08-13 09:57:34 -04003081#if GR_TEST_UTILS
3082 SkString onDumpInfo() const override {
3083 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3084 for (const auto& geo : fRRects) {
3085 string.appendf(
3086 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3087 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3088 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3089 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3090 geo.fInnerXRadius, geo.fInnerYRadius);
3091 }
3092 string += fHelper.dumpInfo();
3093 return string;
3094 }
3095#endif
3096
Brian Salomon05441c42017-05-15 16:45:49 -04003097 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003098 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003099 SkScalar fXRadius;
3100 SkScalar fYRadius;
3101 SkScalar fInnerXRadius;
3102 SkScalar fInnerYRadius;
3103 SkRect fDevBounds;
3104 };
3105
Brian Salomon289e3d82016-12-14 15:52:56 -05003106 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003107 Helper fHelper;
3108 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003109 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003110 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003111 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003112
Chris Daltoneb694b72020-03-16 09:25:50 -06003113 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003114 GrProgramInfo* fProgramInfo = nullptr;
3115
John Stiles7571f9e2020-09-02 22:42:33 -04003116 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003117};
3118
Herb Derbyc76d4092020-10-07 16:46:15 -04003119GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3120 GrPaint&& paint,
3121 const SkMatrix& viewMatrix,
3122 const SkRRect& rrect,
3123 const SkStrokeRec& stroke,
3124 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003125 SkASSERT(viewMatrix.rectStaysRect());
3126 SkASSERT(viewMatrix.isSimilarity());
3127 SkASSERT(rrect.isSimple());
3128 SkASSERT(!rrect.isOval());
3129 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3130
3131 // RRect ops only handle simple, but not too simple, rrects.
3132 // Do any matrix crunching before we reset the draw state for device coords.
3133 const SkRect& rrectBounds = rrect.getBounds();
3134 SkRect bounds;
3135 viewMatrix.mapRect(&bounds, rrectBounds);
3136
3137 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3138 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3139 viewMatrix[SkMatrix::kMSkewY]));
3140
3141 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3142 SkScalar scaledStroke = -1;
3143 SkScalar strokeWidth = stroke.getWidth();
3144 SkStrokeRec::Style style = stroke.getStyle();
3145
3146 bool isStrokeOnly =
3147 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3148 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3149
3150 if (hasStroke) {
3151 if (SkStrokeRec::kHairline_Style == style) {
3152 scaledStroke = SK_Scalar1;
3153 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003154 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3155 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003156 }
3157 }
3158
3159 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3160 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3161 // patch will have fractional coverage. This only matters when the interior is actually filled.
3162 // We could consider falling back to rect rendering here, since a tiny radius is
3163 // indistinguishable from a square corner.
3164 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3165 return nullptr;
3166 }
3167
3168 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3169 scaledStroke, isStrokeOnly);
3170}
3171
Herb Derbyc76d4092020-10-07 16:46:15 -04003172GrOp::Owner make_rrect_op(GrRecordingContext* context,
3173 GrPaint&& paint,
3174 const SkMatrix& viewMatrix,
3175 const SkRRect& rrect,
3176 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003177 SkASSERT(viewMatrix.rectStaysRect());
3178 SkASSERT(rrect.isSimple());
3179 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003180
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003181 // RRect ops only handle simple, but not too simple, rrects.
3182 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003183 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003184 SkRect bounds;
3185 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003186
Mike Reed242135a2018-02-22 13:41:39 -05003187 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003188 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3189 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3190 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3191 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003192
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003194
bsalomon4b4a7cc2016-07-08 04:42:54 -07003195 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3196 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003198
Brian Salomon289e3d82016-12-14 15:52:56 -05003199 bool isStrokeOnly =
3200 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003201 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3202
3203 if (hasStroke) {
3204 if (SkStrokeRec::kHairline_Style == style) {
3205 scaledStroke.set(1, 1);
3206 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003207 scaledStroke.fX = SkScalarAbs(
3208 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3209 scaledStroke.fY = SkScalarAbs(
3210 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003211 }
3212
Jim Van Verth64b85892019-06-17 12:01:46 -04003213 // if half of strokewidth is greater than radius, we don't handle that right now
3214 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3215 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003216 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003217 }
3218 }
3219
Brian Salomon8a97f562019-04-18 14:07:27 -04003220 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003221 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003222 std::swap(xRadius, yRadius);
3223 std::swap(scaledStroke.fX, scaledStroke.fY);
3224 }
3225
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003226 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3227 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3228 // patch will have fractional coverage. This only matters when the interior is actually filled.
3229 // We could consider falling back to rect rendering here, since a tiny radius is
3230 // indistinguishable from a square corner.
3231 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003232 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003233 }
3234
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003236 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3237 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003238}
3239
Herb Derbyc76d4092020-10-07 16:46:15 -04003240GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3241 GrPaint&& paint,
3242 const SkMatrix& viewMatrix,
3243 const SkRRect& rrect,
3244 const SkStrokeRec& stroke,
3245 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003246 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003247 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003248 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003249 }
3250
3251 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003252 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003253 }
3254
Greg Daniel2655ede2019-04-10 00:49:28 +00003255 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003256}
joshualitt3e708c52015-04-30 13:49:27 -07003257
bsalomon4b4a7cc2016-07-08 04:42:54 -07003258///////////////////////////////////////////////////////////////////////////////
3259
Herb Derbyc76d4092020-10-07 16:46:15 -04003260GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3261 GrPaint&& paint,
3262 const SkMatrix& viewMatrix,
3263 const SkRect& oval,
3264 const GrStyle& style,
3265 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003266 SkScalar width = oval.width();
3267 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3268 circle_stays_circle(viewMatrix));
3269
3270 auto r = width / 2.f;
3271 SkPoint center = { oval.centerX(), oval.centerY() };
3272 if (style.hasNonDashPathEffect()) {
3273 return nullptr;
3274 } else if (style.isDashed()) {
3275 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3276 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3277 return nullptr;
3278 }
3279 auto onInterval = style.dashIntervals()[0];
3280 auto offInterval = style.dashIntervals()[1];
3281 if (offInterval == 0) {
3282 GrStyle strokeStyle(style.strokeRec(), nullptr);
3283 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3284 strokeStyle, shaderCaps);
3285 } else if (onInterval == 0) {
3286 // There is nothing to draw but we have no way to indicate that here.
3287 return nullptr;
3288 }
3289 auto angularOnInterval = onInterval / r;
3290 auto angularOffInterval = offInterval / r;
3291 auto phaseAngle = style.dashPhase() / r;
3292 // Currently this function doesn't accept ovals with different start angles, though
3293 // it could.
3294 static const SkScalar kStartAngle = 0.f;
3295 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3296 style.strokeRec().getWidth(), kStartAngle,
3297 angularOnInterval, angularOffInterval, phaseAngle);
3298 }
3299 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3300}
3301
Herb Derbyc76d4092020-10-07 16:46:15 -04003302GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3303 GrPaint&& paint,
3304 const SkMatrix& viewMatrix,
3305 const SkRect& oval,
3306 const GrStyle& style,
3307 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003308 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003309 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003310 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3311 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003312 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003313 }
3314
3315 if (style.pathEffect()) {
3316 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003317 }
3318
Stan Ilieveb868aa2017-02-21 11:06:16 -05003319 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003320 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003321 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003322 }
3323
Stan Ilieveb868aa2017-02-21 11:06:16 -05003324 // Otherwise, if we have shader derivative support, render as device-independent
3325 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003326 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3327 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3328 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3329 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3330 // Check for near-degenerate matrix
3331 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003332 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003333 style.strokeRec());
3334 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003335 }
3336
bsalomon4b4a7cc2016-07-08 04:42:54 -07003337 return nullptr;
3338}
3339
3340///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003341
Herb Derbyc76d4092020-10-07 16:46:15 -04003342GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3343 GrPaint&& paint,
3344 const SkMatrix& viewMatrix,
3345 const SkRect& oval, SkScalar startAngle,
3346 SkScalar sweepAngle, bool useCenter,
3347 const GrStyle& style,
3348 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003349 SkASSERT(!oval.isEmpty());
3350 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003351 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003352 if (SkScalarAbs(sweepAngle) >= 360.f) {
3353 return nullptr;
3354 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003355 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3356 return nullptr;
3357 }
3358 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003359 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3360 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003361 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003362 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003363}
3364
3365///////////////////////////////////////////////////////////////////////////////
3366
Hal Canary6f6961e2017-01-31 13:50:44 -05003367#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003368
Brian Salomon05441c42017-05-15 16:45:49 -04003369GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003370 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003371 SkScalar rotate = random->nextSScalar1() * 360.f;
3372 SkScalar translateX = random->nextSScalar1() * 1000.f;
3373 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003374 SkScalar scale;
3375 do {
3376 scale = random->nextSScalar1() * 100.f;
3377 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003378 SkMatrix viewMatrix;
3379 viewMatrix.setRotate(rotate);
3380 viewMatrix.postTranslate(translateX, translateY);
3381 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003382 SkRect circle = GrTest::TestSquare(random);
3383 SkPoint center = {circle.centerX(), circle.centerY()};
3384 SkScalar radius = circle.width() / 2.f;
3385 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003386 CircleOp::ArcParams arcParamsTmp;
3387 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003388 if (random->nextBool()) {
3389 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003390 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3391 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 arcParams = &arcParamsTmp;
3393 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003394 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3395 center, radius,
3396 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003397 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003398 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003399 }
Mike Klein16885072018-12-11 09:54:31 -05003400 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003401 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003402}
3403
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003404GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3405 SkScalar rotate = random->nextSScalar1() * 360.f;
3406 SkScalar translateX = random->nextSScalar1() * 1000.f;
3407 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003408 SkScalar scale;
3409 do {
3410 scale = random->nextSScalar1() * 100.f;
3411 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003412 SkMatrix viewMatrix;
3413 viewMatrix.setRotate(rotate);
3414 viewMatrix.postTranslate(translateX, translateY);
3415 viewMatrix.postScale(scale, scale);
3416 SkRect circle = GrTest::TestSquare(random);
3417 SkPoint center = {circle.centerX(), circle.centerY()};
3418 SkScalar radius = circle.width() / 2.f;
3419 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3420 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3421 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3422 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3423 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003424 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3425 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003426 startAngle, onAngle, offAngle, phase);
3427}
3428
Brian Salomon05441c42017-05-15 16:45:49 -04003429GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003430 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003431 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003432 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003433 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003434}
3435
Brian Salomon05441c42017-05-15 16:45:49 -04003436GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003437 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003438 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003439 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003440 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003441}
3442
Jim Van Verth64b85892019-06-17 12:01:46 -04003443GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3444 do {
3445 SkScalar rotate = random->nextSScalar1() * 360.f;
3446 SkScalar translateX = random->nextSScalar1() * 1000.f;
3447 SkScalar translateY = random->nextSScalar1() * 1000.f;
3448 SkScalar scale;
3449 do {
3450 scale = random->nextSScalar1() * 100.f;
3451 } while (scale == 0);
3452 SkMatrix viewMatrix;
3453 viewMatrix.setRotate(rotate);
3454 viewMatrix.postTranslate(translateX, translateY);
3455 viewMatrix.postScale(scale, scale);
3456 SkRect rect = GrTest::TestRect(random);
3457 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3458 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3459 if (rrect.isOval()) {
3460 continue;
3461 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003462 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003463 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3464 GrTest::TestStrokeRec(random), nullptr);
3465 if (op) {
3466 return op;
3467 }
3468 assert_alive(paint);
3469 } while (true);
3470}
3471
Brian Salomon05441c42017-05-15 16:45:49 -04003472GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003473 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003474 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003475 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003476 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003477}
3478
3479#endif