blob: edf9188e07ccf06f434e2b81e3237e7627e89999 [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 };
John Stiles6b58a332020-10-26 17:53:06 -0400400 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
401 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400402 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400403 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);
John Stiles6b58a332020-10-26 17:53:06 -0400407 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 fragBuilder->codeAppend(R"(
409 float d = length(circleEdge.xy) * circleEdge.z;
410
411 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500412 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400413 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500414 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400415 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 edgeAlpha *= innerAlpha;
417
Ethan Nicholase1f55022019-02-05 17:17:40 -0500418 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400419 angleFromStart = mod(angleFromStart, 6.28318530718);
420 float x = mod(angleFromStart, dashParams.y);
421 // Convert the radial distance from center to pixel into a diameter.
422 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500423 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
424 half(dashParams.w));
425 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
426 half(dashParams.y) + half(dashParams.x) -
427 half(dashParams.w));
428 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
429 half(-dashParams.y) + half(dashParams.x) -
430 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400431 half dashAlpha = 0;
432 )");
433 fragBuilder->codeAppendf(R"(
434 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500435 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 currDash.y = min(currDash.y, lastIntervalLength);
437 if (nextDash.x >= lastIntervalLength) {
438 // The next dash is outside the 0..2pi range, throw it away
439 nextDash.xy = half2(1000);
440 } else {
441 // Clip the end of the next dash to the end of the circle
442 nextDash.y = min(nextDash.y, lastIntervalLength);
443 }
444 }
445 )", fnName.c_str(), fnName.c_str());
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.x = max(currDash.x, 0);
450 if (prevDash.y <= 0) {
451 // The previous dash is outside the 0..2pi range, throw it away
452 prevDash.xy = half2(1000);
453 } else {
454 // Clip the start previous dash to the start of the circle
455 prevDash.x = max(prevDash.x, 0);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500460 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
461 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
462 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400463 dashAlpha = min(dashAlpha, 1);
464 edgeAlpha *= dashAlpha;
465 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
466 fnName.c_str());
467 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
468 }
469
470 static void GenKey(const GrGeometryProcessor& gp,
471 const GrShaderCaps&,
472 GrProcessorKeyBuilder* b) {
473 const ButtCapDashedCircleGeometryProcessor& bcscgp =
474 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400475 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 }
477
Brian Osman609f1592020-07-01 15:14:39 -0400478 void setData(const GrGLSLProgramDataManager& pdman,
479 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400480 this->setTransform(pdman, fLocalMatrixUniform,
481 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
482 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483 }
484
485 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400486 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400487
488 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
489 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400490 };
491
492 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500493 Attribute fInPosition;
494 Attribute fInColor;
495 Attribute fInCircleEdge;
496 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400497
498 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
499
John Stiles7571f9e2020-09-02 22:42:33 -0400500 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501};
502
503#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500504GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500505 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400506 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500507 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508}
509#endif
510
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000511///////////////////////////////////////////////////////////////////////////////
512
513/**
514 * 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 +0000515 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
516 * in both x and y directions.
517 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000518 * 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 +0000519 */
520
bsalomoncdaa97b2016-03-08 08:30:14 -0800521class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000522public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500523 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
524 bool useScale, const SkMatrix& localMatrix) {
525 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
526 }
527
528 ~EllipseGeometryProcessor() override {}
529
530 const char* name() const override { return "EllipseGeometryProcessor"; }
531
532 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
533 GLSLProcessor::GenKey(*this, caps, b);
534 }
535
536 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
537 return new GLSLProcessor();
538 }
539
540private:
541 friend class ::SkArenaAlloc; // for access to ctor
542
Greg Daniel2655ede2019-04-10 00:49:28 +0000543 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400544 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500545 : INHERITED(kEllipseGeometryProcessor_ClassID)
546 , fLocalMatrix(localMatrix)
547 , fStroke(stroke)
548 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500549 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500550 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400551 if (useScale) {
552 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
553 } else {
554 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
555 }
556 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500557 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000558 }
559
egdaniel57d3b032015-11-13 11:57:27 -0800560 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800562 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563
Brian Salomon289e3d82016-12-14 15:52:56 -0500564 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800565 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800566 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800567 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800568 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000569
joshualittabb52a12015-01-13 15:02:10 -0800570 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800571 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800572
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400573 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
574 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800575 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800576 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500577 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000578
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400579 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800580 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800582
Chris Dalton60283612018-02-14 13:38:14 -0700583 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700584 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500585 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800586
joshualittabb52a12015-01-13 15:02:10 -0800587 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500588 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400589 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
590 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
591 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800592
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400593 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
594 // to compute both the edges because we need two separate test equations for
595 // the single offset.
596 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
597 // the distance by the gradient, non-uniformly scaled by the inverse of the
598 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800599
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400600 // On medium precision devices, we scale the denominator of the distance equation
601 // before taking the inverse square root to minimize the chance that we're dividing
602 // by zero, then we scale the result back.
603
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000604 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400605 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400606 if (egp.fStroke) {
607 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
608 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400609 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
610 if (egp.fUseScale) {
611 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
612 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
613 } else {
614 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
615 }
616 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700617
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000618 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400619 if (args.fShaderCaps->floatIs32Bits()) {
620 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
621 } else {
622 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
623 }
624 if (egp.fUseScale) {
625 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
626 ellipseOffsets.fsIn());
627 } else {
628 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
629 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000630 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000631
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000632 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800633 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400634 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800635 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400636 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400637 if (egp.fUseScale) {
638 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
639 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
640 } else {
641 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
642 }
643 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
644 if (!args.fShaderCaps->floatIs32Bits()) {
645 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
646 }
647 if (egp.fUseScale) {
648 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
649 ellipseOffsets.fsIn());
650 } else {
651 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
652 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000653 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654 }
655
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400656 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000657 }
658
robertphillips46d36f02015-01-18 08:14:14 -0800659 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500660 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700661 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800662 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400663 uint32_t key = egp.fStroke ? 0x1 : 0x0;
664 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700665 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666 }
667
Brian Osman609f1592020-07-01 15:14:39 -0400668 void setData(const GrGLSLProgramDataManager& pdman,
669 const GrPrimitiveProcessor& primProc) override {
bsalomona624bf32016-09-20 09:12:47 -0700670 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400671 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700672 }
673
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000674 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400675 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400676
677 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
678 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679 };
680
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500681 Attribute fInPosition;
682 Attribute fInColor;
683 Attribute fInEllipseOffset;
684 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400685
joshualitte3ababe2015-05-15 07:56:07 -0700686 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000687 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400688 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400690 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691
John Stiles7571f9e2020-09-02 22:42:33 -0400692 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693};
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
Hal Canary6f6961e2017-01-31 13:50:44 -0500697#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500698GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
699 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
700 d->fRandom->nextBool(), d->fRandom->nextBool(),
701 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000702}
Hal Canary6f6961e2017-01-31 13:50:44 -0500703#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704
705///////////////////////////////////////////////////////////////////////////////
706
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000707/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000708 * 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 +0000709 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
710 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
711 * using differentials.
712 *
713 * The result is device-independent and can be used with any affine matrix.
714 */
715
bsalomoncdaa97b2016-03-08 08:30:14 -0800716enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000717
bsalomoncdaa97b2016-03-08 08:30:14 -0800718class DIEllipseGeometryProcessor : public GrGeometryProcessor {
719public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500720 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
721 const SkMatrix& viewMatrix, DIEllipseStyle style) {
722 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
723 }
724
725 ~DIEllipseGeometryProcessor() override {}
726
727 const char* name() const override { return "DIEllipseGeometryProcessor"; }
728
729 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
730 GLSLProcessor::GenKey(*this, caps, b);
731 }
732
733 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
734 return new GLSLProcessor();
735 }
736
737private:
738 friend class ::SkArenaAlloc; // for access to ctor
739
Greg Daniel2655ede2019-04-10 00:49:28 +0000740 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400741 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400742 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400743 , fViewMatrix(viewMatrix)
744 , fUseScale(useScale)
745 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500746 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500747 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400748 if (useScale) {
749 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
750 kFloat3_GrSLType};
751 } else {
752 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
753 kFloat2_GrSLType};
754 }
755 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500756 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 }
758
egdaniel57d3b032015-11-13 11:57:27 -0800759 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500761 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762
joshualitt465283c2015-09-11 08:19:35 -0700763 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800764 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800765 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800766 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800767 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000768
joshualittabb52a12015-01-13 15:02:10 -0800769 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800770 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800771
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400772 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
773 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800774 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500775 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700776
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400777 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800778 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800780
Chris Dalton60283612018-02-14 13:38:14 -0700781 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500782 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800783
joshualittabb52a12015-01-13 15:02:10 -0800784 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400785 this->writeOutputPosition(vertBuilder,
786 uniformHandler,
787 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500788 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400789 diegp.fViewMatrix,
790 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400791 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800792
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400794 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
795 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
796 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
797 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500798 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
800 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400802 if (diegp.fUseScale) {
803 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
804 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000805
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400806 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000807 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400808 if (args.fShaderCaps->floatIs32Bits()) {
809 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
810 } else {
811 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
812 }
813 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
814 if (diegp.fUseScale) {
815 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
816 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800817 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000818 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000819 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
820 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000821 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000822 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000823 }
824
825 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800826 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800827 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
828 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400829 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
830 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500831 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400832 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
833 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500834 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 if (diegp.fUseScale) {
836 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
837 }
838 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
839 if (!args.fShaderCaps->floatIs32Bits()) {
840 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
841 }
842 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
843 if (diegp.fUseScale) {
844 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
845 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000846 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000848
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400849 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 }
851
robertphillips46d36f02015-01-18 08:14:14 -0800852 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500853 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700854 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800855 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400856 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
857 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700858 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000859 }
860
Brian Osman609f1592020-07-01 15:14:39 -0400861 void setData(const GrGLSLProgramDataManager& pdman,
862 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800863 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700864
Michael Ludwig553db622020-06-19 10:47:30 -0400865 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000866 }
867
868 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400869 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700870 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800871
John Stiles7571f9e2020-09-02 22:42:33 -0400872 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000873 };
874
Brian Salomon92be2f72018-06-19 14:33:47 -0400875
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500876 Attribute fInPosition;
877 Attribute fInColor;
878 Attribute fInEllipseOffsets0;
879 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400880
Brian Salomon289e3d82016-12-14 15:52:56 -0500881 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400882 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500883 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000884
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400885 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886
John Stiles7571f9e2020-09-02 22:42:33 -0400887 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888};
889
bsalomoncdaa97b2016-03-08 08:30:14 -0800890GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000891
Hal Canary6f6961e2017-01-31 13:50:44 -0500892#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500893GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
894 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
895 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
896 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897}
Hal Canary6f6961e2017-01-31 13:50:44 -0500898#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000899
900///////////////////////////////////////////////////////////////////////////////
901
jvanverth6ca48822016-10-07 06:57:32 -0700902// We have two possible cases for geometry for a circle:
903
904// In the case of a normal fill, we draw geometry for the circle as an octagon.
905static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500906 // enter the octagon
907 // clang-format off
908 0, 1, 8, 1, 2, 8,
909 2, 3, 8, 3, 4, 8,
910 4, 5, 8, 5, 6, 8,
911 6, 7, 8, 7, 0, 8
912 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700913};
914
915// For stroked circles, we use two nested octagons.
916static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500917 // enter the octagon
918 // clang-format off
919 0, 1, 9, 0, 9, 8,
920 1, 2, 10, 1, 10, 9,
921 2, 3, 11, 2, 11, 10,
922 3, 4, 12, 3, 12, 11,
923 4, 5, 13, 4, 13, 12,
924 5, 6, 14, 5, 14, 13,
925 6, 7, 15, 6, 15, 14,
926 7, 0, 8, 7, 8, 15,
927 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700928};
929
Brian Osman9d958b52018-11-13 12:46:56 -0500930// Normalized geometry for octagons that circumscribe and lie on a circle:
931
932static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
933static constexpr SkPoint kOctagonOuter[] = {
934 SkPoint::Make(-kOctOffset, -1),
935 SkPoint::Make( kOctOffset, -1),
936 SkPoint::Make( 1, -kOctOffset),
937 SkPoint::Make( 1, kOctOffset),
938 SkPoint::Make( kOctOffset, 1),
939 SkPoint::Make(-kOctOffset, 1),
940 SkPoint::Make(-1, kOctOffset),
941 SkPoint::Make(-1, -kOctOffset),
942};
943
944// cosine and sine of pi/8
945static constexpr SkScalar kCosPi8 = 0.923579533f;
946static constexpr SkScalar kSinPi8 = 0.382683432f;
947static constexpr SkPoint kOctagonInner[] = {
948 SkPoint::Make(-kSinPi8, -kCosPi8),
949 SkPoint::Make( kSinPi8, -kCosPi8),
950 SkPoint::Make( kCosPi8, -kSinPi8),
951 SkPoint::Make( kCosPi8, kSinPi8),
952 SkPoint::Make( kSinPi8, kCosPi8),
953 SkPoint::Make(-kSinPi8, kCosPi8),
954 SkPoint::Make(-kCosPi8, kSinPi8),
955 SkPoint::Make(-kCosPi8, -kSinPi8),
956};
Brian Salomon289e3d82016-12-14 15:52:56 -0500957
jvanverth6ca48822016-10-07 06:57:32 -0700958static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
959static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
960static const int kVertsPerStrokeCircle = 16;
961static const int kVertsPerFillCircle = 9;
962
963static int circle_type_to_vert_count(bool stroked) {
964 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
965}
966
967static int circle_type_to_index_count(bool stroked) {
968 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
969}
970
971static const uint16_t* circle_type_to_indices(bool stroked) {
972 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
973}
974
975///////////////////////////////////////////////////////////////////////////////
976
Brian Salomon05441c42017-05-15 16:45:49 -0400977class CircleOp final : public GrMeshDrawOp {
978private:
979 using Helper = GrSimpleMeshDrawOpHelper;
980
joshualitt76e7fb62015-02-11 08:52:27 -0800981public:
Brian Salomon25a88092016-12-01 09:36:50 -0500982 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700983
bsalomon4f3a0ca2016-08-22 13:14:26 -0700984 /** Optional extra params to render a partial arc rather than a full circle. */
985 struct ArcParams {
986 SkScalar fStartAngleRadians;
987 SkScalar fSweepAngleRadians;
988 bool fUseCenter;
989 };
Brian Salomon05441c42017-05-15 16:45:49 -0400990
Herb Derbyc76d4092020-10-07 16:46:15 -0400991 static GrOp::Owner Make(GrRecordingContext* context,
992 GrPaint&& paint,
993 const SkMatrix& viewMatrix,
994 SkPoint center,
995 SkScalar radius,
996 const GrStyle& style,
997 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 if (style.hasPathEffect()) {
1000 return nullptr;
1001 }
Brian Salomon05441c42017-05-15 16:45:49 -04001002 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001003 SkStrokeRec::Style recStyle = stroke.getStyle();
1004 if (arcParams) {
1005 // Arc support depends on the style.
1006 switch (recStyle) {
1007 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001008 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001009 return nullptr;
1010 case SkStrokeRec::kFill_Style:
1011 // This supports all fills.
1012 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001013 case SkStrokeRec::kStroke_Style:
1014 // Strokes that don't use the center point are supported with butt and round
1015 // caps.
1016 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1017 return nullptr;
1018 }
1019 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001021 // Hairline only supports butt cap. Round caps could be emulated by slightly
1022 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001023 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1024 return nullptr;
1025 }
1026 break;
1027 }
1028 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001029 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1030 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001031 }
1032
Herb Derbyc76d4092020-10-07 16:46:15 -04001033 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001034 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1035 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001036 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001037 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001038 const SkStrokeRec& stroke = style.strokeRec();
1039 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001040
Brian Salomon45c92202018-04-10 10:53:58 -04001041 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001042
bsalomon4b4a7cc2016-07-08 04:42:54 -07001043 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001044 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001045 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001046
Brian Salomon289e3d82016-12-14 15:52:56 -05001047 bool isStrokeOnly =
1048 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001050
jvanverth6ca48822016-10-07 06:57:32 -07001051 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001052 SkScalar outerRadius = radius;
1053 SkScalar halfWidth = 0;
1054 if (hasStroke) {
1055 if (SkScalarNearlyZero(strokeWidth)) {
1056 halfWidth = SK_ScalarHalf;
1057 } else {
1058 halfWidth = SkScalarHalf(strokeWidth);
1059 }
1060
1061 outerRadius += halfWidth;
1062 if (isStrokeOnly) {
1063 innerRadius = radius - halfWidth;
1064 }
1065 }
1066
1067 // The radii are outset for two reasons. First, it allows the shader to simply perform
1068 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1069 // Second, the outer radius is used to compute the verts of the bounding box that is
1070 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001071 outerRadius += SK_ScalarHalf;
1072 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001073 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001074 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001075
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 // This makes every point fully inside the intersection plane.
1077 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1078 // This makes every point fully outside the union plane.
1079 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001080 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001081 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1082 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001083 if (arcParams) {
1084 // The shader operates in a space where the circle is translated to be centered at the
1085 // origin. Here we compute points on the unit circle at the starting and ending angles.
1086 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001087 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1088 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001089 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001090 stopPoint.fY = SkScalarSin(endAngle);
1091 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001092
1093 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1094 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1095 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1096 startPoint.normalize();
1097 stopPoint.normalize();
1098
Brian Salomon3517aa72019-12-11 08:16:22 -05001099 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1100 // should orient the clip planes for arcs.
1101 SkASSERT(viewMatrix.isSimilarity());
1102 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1103 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1104 if (upperLeftDet < 0) {
1105 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001106 }
1107
Brian Salomon45c92202018-04-10 10:53:58 -04001108 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1109 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1110 SkPoint roundCaps[2];
1111 if (fRoundCaps) {
1112 // Compute the cap center points in the normalized space.
1113 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1114 roundCaps[0] = startPoint * midRadius;
1115 roundCaps[1] = stopPoint * midRadius;
1116 } else {
1117 roundCaps[0] = kUnusedRoundCaps[0];
1118 roundCaps[1] = kUnusedRoundCaps[1];
1119 }
1120
bsalomon4f3a0ca2016-08-22 13:14:26 -07001121 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001122 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1123 // center of the butts.
1124 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001126 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001128 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1129 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1130 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001131 if (useCenter) {
1132 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1133 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001134 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1135 if (arcParams->fSweepAngleRadians < 0) {
1136 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001137 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001138 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001139 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001140 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001141 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 color,
1143 innerRadius,
1144 outerRadius,
1145 {norm0.fX, norm0.fY, 0.5f},
1146 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1147 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001148 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001149 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001150 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001151 fClipPlaneIsect = false;
1152 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001153 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001154 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001155 color,
1156 innerRadius,
1157 outerRadius,
1158 {norm0.fX, norm0.fY, 0.5f},
1159 {norm1.fX, norm1.fY, 0.5f},
1160 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001161 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001162 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001163 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001164 fClipPlaneIsect = true;
1165 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001166 }
1167 } else {
1168 // We clip to a secant of the original circle.
1169 startPoint.scale(radius);
1170 stopPoint.scale(radius);
1171 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1172 norm.normalize();
1173 if (arcParams->fSweepAngleRadians > 0) {
1174 norm.negate();
1175 }
1176 SkScalar d = -norm.dot(startPoint) + 0.5f;
1177
Brian Salomon05441c42017-05-15 16:45:49 -04001178 fCircles.emplace_back(
1179 Circle{color,
1180 innerRadius,
1181 outerRadius,
1182 {norm.fX, norm.fY, d},
1183 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1184 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001185 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001186 devBounds,
1187 stroked});
1188 fClipPlane = true;
1189 fClipPlaneIsect = false;
1190 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001191 }
1192 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fCircles.emplace_back(
1194 Circle{color,
1195 innerRadius,
1196 outerRadius,
1197 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001200 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001201 devBounds,
1202 stroked});
1203 fClipPlane = false;
1204 fClipPlaneIsect = false;
1205 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001206 }
bsalomon88cf17d2016-07-08 06:40:56 -07001207 // Use the original radius and stroke radius for the bounds so that it does not include the
1208 // AA bloat.
1209 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001210 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001211 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001212 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001213 fVertCount = circle_type_to_vert_count(stroked);
1214 fIndexCount = circle_type_to_index_count(stroked);
1215 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001216 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001217
Brian Salomon289e3d82016-12-14 15:52:56 -05001218 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001219
Chris Dalton1706cbf2019-05-21 19:35:29 -06001220 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001221 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001222 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001223 } else {
1224 fHelper.visitProxies(func);
1225 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001226 }
1227
Chris Dalton6ce447a2019-06-23 18:07:38 -06001228 GrProcessorSet::Analysis finalize(
1229 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1230 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001231 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001232 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001233 GrProcessorAnalysisCoverage::kSingleChannel, color,
1234 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001235 }
1236
1237 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1238
bsalomone46f9fe2015-08-18 06:05:14 -07001239private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001240 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001241
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 void onCreateProgramInfo(const GrCaps* caps,
1243 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001244 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001245 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001246 const GrXferProcessor::DstProxyView& dstProxyView,
1247 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001248 SkMatrix localMatrix;
1249 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001250 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001251 }
1252
Robert Phillips4490d922020-03-03 14:50:59 -05001253 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001254 fClipPlaneIsect, fClipPlaneUnion,
1255 fRoundCaps, fWideColor,
1256 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001257
Brian Salomon8afde5f2020-04-01 16:22:00 -04001258 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001259 dstProxyView, gp, GrPrimitiveType::kTriangles,
1260 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001261 }
1262
Robert Phillips4490d922020-03-03 14:50:59 -05001263 void onPrepareDraws(Target* target) override {
1264 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001265 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001266 if (!fProgramInfo) {
1267 return;
1268 }
1269 }
1270
Brian Salomon12d22642019-01-29 14:38:50 -05001271 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001272 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001273 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1274 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001275 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001276 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001277 return;
1278 }
1279
Brian Salomon12d22642019-01-29 14:38:50 -05001280 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001281 int firstIndex = 0;
1282 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1283 if (!indices) {
1284 SkDebugf("Could not allocate indices\n");
1285 return;
1286 }
1287
1288 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001289 for (const auto& circle : fCircles) {
1290 SkScalar innerRadius = circle.fInnerRadius;
1291 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001292 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001293 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001294
joshualitt76e7fb62015-02-11 08:52:27 -08001295 // The inner radius in the vertex data must be specified in normalized space.
1296 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001297 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001298
1299 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001300 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001301
Brian Osman9a24fee2018-08-03 09:48:42 -04001302 SkVector geoClipPlane = { 0, 0 };
1303 SkScalar offsetClipDist = SK_Scalar1;
1304 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1305 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1306 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1307 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1308 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1309 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1310 // the AA can extend just past the center of the circle.
1311 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1312 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1313 SkAssertResult(geoClipPlane.normalize());
1314 offsetClipDist = 0.5f / halfWidth;
1315 }
1316
Brian Osman7d8f82b2018-11-08 10:24:09 -05001317 for (int i = 0; i < 8; ++i) {
1318 // This clips the normalized offset to the half-plane we computed above. Then we
1319 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001320 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001321 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001322 vertices.write(center + offset * halfWidth,
1323 color,
1324 offset,
1325 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001326 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001327 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001328 }
1329 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001330 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001331 }
1332 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001334 }
1335 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001336 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001337 }
Brian Salomon45c92202018-04-10 10:53:58 -04001338 }
jvanverth6ca48822016-10-07 06:57:32 -07001339
Brian Salomon05441c42017-05-15 16:45:49 -04001340 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001341 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001342
Brian Osman7d8f82b2018-11-08 10:24:09 -05001343 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001344 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1345 color,
1346 kOctagonInner[i] * innerRadius,
1347 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
1354 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
1357 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 }
Brian Salomon45c92202018-04-10 10:53:58 -04001360 }
jvanverth6ca48822016-10-07 06:57:32 -07001361 } else {
1362 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001364 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001366 }
jvanverth6ca48822016-10-07 06:57:32 -07001367 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001369 }
1370 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001372 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001373 if (fRoundCaps) {
1374 vertices.write(circle.fRoundCapCenters);
1375 }
jvanverth6ca48822016-10-07 06:57:32 -07001376 }
1377
Brian Salomon05441c42017-05-15 16:45:49 -04001378 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1379 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001380 for (int i = 0; i < primIndexCount; ++i) {
1381 *indices++ = primIndices[i] + currStartVertex;
1382 }
1383
Brian Salomon05441c42017-05-15 16:45:49 -04001384 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001385 }
jvanverth6ca48822016-10-07 06:57:32 -07001386
Robert Phillips4490d922020-03-03 14:50:59 -05001387 fMesh = target->allocMesh();
1388 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001389 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001390 }
1391
1392 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001393 if (!fProgramInfo || !fMesh) {
1394 return;
1395 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001396
Chris Dalton765ed362020-03-16 17:34:44 -06001397 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1398 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1399 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001400 }
1401
Herb Derbye25c3002020-10-27 15:57:27 -04001402 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001403 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001404
1405 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001406 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001407 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001408 }
1409
Brian Salomon05441c42017-05-15 16:45:49 -04001410 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001411 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001412 }
1413
Brian Salomon05441c42017-05-15 16:45:49 -04001414 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001415 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1416 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001417 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001418 }
1419
Brian Salomon289e3d82016-12-14 15:52:56 -05001420 // Because we've set up the ops that don't use the planes with noop values
1421 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001422 fClipPlane |= that->fClipPlane;
1423 fClipPlaneIsect |= that->fClipPlaneIsect;
1424 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001425 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001426 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001427
Brian Salomon05441c42017-05-15 16:45:49 -04001428 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001429 fVertCount += that->fVertCount;
1430 fIndexCount += that->fIndexCount;
1431 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001432 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001433 }
1434
John Stilesaf366522020-08-13 09:57:34 -04001435#if GR_TEST_UTILS
1436 SkString onDumpInfo() const override {
1437 SkString string;
1438 for (int i = 0; i < fCircles.count(); ++i) {
1439 string.appendf(
1440 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1441 "InnerRad: %.2f, OuterRad: %.2f\n",
1442 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1443 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1444 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1445 fCircles[i].fOuterRadius);
1446 }
1447 string += fHelper.dumpInfo();
1448 return string;
1449 }
1450#endif
1451
Brian Salomon05441c42017-05-15 16:45:49 -04001452 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001453 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 SkScalar fInnerRadius;
1455 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001456 SkScalar fClipPlane[3];
1457 SkScalar fIsectPlane[3];
1458 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001459 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001460 SkRect fDevBounds;
1461 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462 };
1463
Brian Salomon289e3d82016-12-14 15:52:56 -05001464 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001465 Helper fHelper;
1466 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001467 int fVertCount;
1468 int fIndexCount;
1469 bool fAllFill;
1470 bool fClipPlane;
1471 bool fClipPlaneIsect;
1472 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001473 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001474 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001475
Chris Daltoneb694b72020-03-16 09:25:50 -06001476 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001477 GrProgramInfo* fProgramInfo = nullptr;
1478
John Stiles7571f9e2020-09-02 22:42:33 -04001479 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001480};
1481
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001482class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1483private:
1484 using Helper = GrSimpleMeshDrawOpHelper;
1485
1486public:
1487 DEFINE_OP_CLASS_ID
1488
Herb Derbyc76d4092020-10-07 16:46:15 -04001489 static GrOp::Owner Make(GrRecordingContext* context,
1490 GrPaint&& paint,
1491 const SkMatrix& viewMatrix,
1492 SkPoint center,
1493 SkScalar radius,
1494 SkScalar strokeWidth,
1495 SkScalar startAngle,
1496 SkScalar onAngle,
1497 SkScalar offAngle,
1498 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001499 SkASSERT(circle_stays_circle(viewMatrix));
1500 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001501 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1502 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001503 onAngle, offAngle, phaseAngle);
1504 }
1505
Herb Derbyc76d4092020-10-07 16:46:15 -04001506 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001507 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1508 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1509 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001510 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001511 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001512 SkASSERT(circle_stays_circle(viewMatrix));
1513 viewMatrix.mapPoints(&center, 1);
1514 radius = viewMatrix.mapRadius(radius);
1515 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1516
1517 // Determine the angle where the circle starts in device space and whether its orientation
1518 // has been reversed.
1519 SkVector start;
1520 bool reflection;
1521 if (!startAngle) {
1522 start = {1, 0};
1523 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001524 start.fY = SkScalarSin(startAngle);
1525 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526 }
1527 viewMatrix.mapVectors(&start, 1);
1528 startAngle = SkScalarATan2(start.fY, start.fX);
1529 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1530 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1531
1532 auto totalAngle = onAngle + offAngle;
1533 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1534
1535 SkScalar halfWidth = 0;
1536 if (SkScalarNearlyZero(strokeWidth)) {
1537 halfWidth = SK_ScalarHalf;
1538 } else {
1539 halfWidth = SkScalarHalf(strokeWidth);
1540 }
1541
1542 SkScalar outerRadius = radius + halfWidth;
1543 SkScalar innerRadius = radius - halfWidth;
1544
1545 // The radii are outset for two reasons. First, it allows the shader to simply perform
1546 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1547 // Second, the outer radius is used to compute the verts of the bounding box that is
1548 // rendered and the outset ensures the box will cover all partially covered by the circle.
1549 outerRadius += SK_ScalarHalf;
1550 innerRadius -= SK_ScalarHalf;
1551 fViewMatrixIfUsingLocalCoords = viewMatrix;
1552
1553 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1554 center.fX + outerRadius, center.fY + outerRadius);
1555
1556 // We store whether there is a reflection as a negative total angle.
1557 if (reflection) {
1558 totalAngle = -totalAngle;
1559 }
1560 fCircles.push_back(Circle{
1561 color,
1562 outerRadius,
1563 innerRadius,
1564 onAngle,
1565 totalAngle,
1566 startAngle,
1567 phaseAngle,
1568 devBounds
1569 });
1570 // Use the original radius and stroke radius for the bounds so that it does not include the
1571 // AA bloat.
1572 radius += halfWidth;
1573 this->setBounds(
1574 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001575 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001576 fVertCount = circle_type_to_vert_count(true);
1577 fIndexCount = circle_type_to_index_count(true);
1578 }
1579
1580 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1581
Chris Dalton1706cbf2019-05-21 19:35:29 -06001582 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001583 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001584 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001585 } else {
1586 fHelper.visitProxies(func);
1587 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001588 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001589
Chris Dalton6ce447a2019-06-23 18:07:38 -06001590 GrProcessorSet::Analysis finalize(
1591 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1592 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001593 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001594 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001595 GrProcessorAnalysisCoverage::kSingleChannel, color,
1596 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001597 }
1598
1599 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1600
1601private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001602 GrProgramInfo* programInfo() override { return fProgramInfo; }
1603
Robert Phillips4133dc42020-03-11 15:55:55 -04001604 void onCreateProgramInfo(const GrCaps* caps,
1605 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001606 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001607 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001608 const GrXferProcessor::DstProxyView& dstProxyView,
1609 GrXferBarrierFlags renderPassXferBarriers) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001610 SkMatrix localMatrix;
1611 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001612 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001613 }
1614
1615 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001616 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001617 fWideColor,
1618 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001619
Brian Salomon8afde5f2020-04-01 16:22:00 -04001620 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001621 dstProxyView, gp, GrPrimitiveType::kTriangles,
1622 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001623 }
1624
Robert Phillips4490d922020-03-03 14:50:59 -05001625 void onPrepareDraws(Target* target) override {
1626 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001627 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001628 if (!fProgramInfo) {
1629 return;
1630 }
1631 }
1632
Brian Salomon12d22642019-01-29 14:38:50 -05001633 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001634 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001635 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1636 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001637 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001638 SkDebugf("Could not allocate vertices\n");
1639 return;
1640 }
1641
Brian Salomon12d22642019-01-29 14:38:50 -05001642 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001643 int firstIndex = 0;
1644 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1645 if (!indices) {
1646 SkDebugf("Could not allocate indices\n");
1647 return;
1648 }
1649
1650 int currStartVertex = 0;
1651 for (const auto& circle : fCircles) {
1652 // The inner radius in the vertex data must be specified in normalized space so that
1653 // length() can be called with smaller values to avoid precision issues with half
1654 // floats.
1655 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1656 const SkRect& bounds = circle.fDevBounds;
1657 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001658 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1659 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1660 };
1661 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001662 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001663 dashParams.totalAngle = -dashParams.totalAngle;
1664 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001665 }
1666
Brian Osmane3caf2d2018-11-21 13:48:36 -05001667 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001668
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001669 // The bounding geometry for the circle is composed of an outer bounding octagon and
1670 // an inner bounded octagon.
1671
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001672 // Compute the vertices of the outer octagon.
1673 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1674 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001675
1676 auto reflectY = [=](const SkPoint& p) {
1677 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001678 };
Brian Osman9d958b52018-11-13 12:46:56 -05001679
1680 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001681 vertices.write(center + kOctagonOuter[i] * halfWidth,
1682 color,
1683 reflectY(kOctagonOuter[i]),
1684 circle.fOuterRadius,
1685 normInnerRadius,
1686 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001687 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001688
1689 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001690 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001691 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1692 color,
1693 reflectY(kOctagonInner[i]) * normInnerRadius,
1694 circle.fOuterRadius,
1695 normInnerRadius,
1696 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001697 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001698
1699 const uint16_t* primIndices = circle_type_to_indices(true);
1700 const int primIndexCount = circle_type_to_index_count(true);
1701 for (int i = 0; i < primIndexCount; ++i) {
1702 *indices++ = primIndices[i] + currStartVertex;
1703 }
1704
1705 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001706 }
1707
Robert Phillips4490d922020-03-03 14:50:59 -05001708 fMesh = target->allocMesh();
1709 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001710 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001711 }
1712
1713 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001714 if (!fProgramInfo || !fMesh) {
1715 return;
1716 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001717
Chris Dalton765ed362020-03-16 17:34:44 -06001718 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1719 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1720 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721 }
1722
Herb Derbye25c3002020-10-27 15:57:27 -04001723 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001724 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1725
1726 // can only represent 65535 unique vertices with 16-bit indices
1727 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001728 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001729 }
1730
1731 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001732 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001733 }
1734
1735 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001736 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1737 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001738 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001739 }
1740
1741 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001742 fVertCount += that->fVertCount;
1743 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001744 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001745 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001746 }
1747
John Stilesaf366522020-08-13 09:57:34 -04001748#if GR_TEST_UTILS
1749 SkString onDumpInfo() const override {
1750 SkString string;
1751 for (int i = 0; i < fCircles.count(); ++i) {
1752 string.appendf(
1753 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1754 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1755 "Phase: %.2f\n",
1756 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1757 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1758 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1759 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1760 fCircles[i].fPhaseAngle);
1761 }
1762 string += fHelper.dumpInfo();
1763 return string;
1764 }
1765#endif
1766
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001767 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001768 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001769 SkScalar fOuterRadius;
1770 SkScalar fInnerRadius;
1771 SkScalar fOnAngle;
1772 SkScalar fTotalAngle;
1773 SkScalar fStartAngle;
1774 SkScalar fPhaseAngle;
1775 SkRect fDevBounds;
1776 };
1777
1778 SkMatrix fViewMatrixIfUsingLocalCoords;
1779 Helper fHelper;
1780 SkSTArray<1, Circle, true> fCircles;
1781 int fVertCount;
1782 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001783 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001784
Chris Daltoneb694b72020-03-16 09:25:50 -06001785 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001786 GrProgramInfo* fProgramInfo = nullptr;
1787
John Stiles7571f9e2020-09-02 22:42:33 -04001788 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001789};
1790
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001791///////////////////////////////////////////////////////////////////////////////
1792
Brian Salomon05441c42017-05-15 16:45:49 -04001793class EllipseOp : public GrMeshDrawOp {
1794private:
1795 using Helper = GrSimpleMeshDrawOpHelper;
1796
1797 struct DeviceSpaceParams {
1798 SkPoint fCenter;
1799 SkScalar fXRadius;
1800 SkScalar fYRadius;
1801 SkScalar fInnerXRadius;
1802 SkScalar fInnerYRadius;
1803 };
1804
joshualitt76e7fb62015-02-11 08:52:27 -08001805public:
Brian Salomon25a88092016-12-01 09:36:50 -05001806 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001807
Herb Derbyc76d4092020-10-07 16:46:15 -04001808 static GrOp::Owner Make(GrRecordingContext* context,
1809 GrPaint&& paint,
1810 const SkMatrix& viewMatrix,
1811 const SkRect& ellipse,
1812 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001813 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001814 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001815 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1816 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001817 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1818 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001819 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1820 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1821 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1822 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001823
bsalomon4b4a7cc2016-07-08 04:42:54 -07001824 // do (potentially) anisotropic mapping of stroke
1825 SkVector scaledStroke;
1826 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001827 scaledStroke.fX = SkScalarAbs(
1828 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1829 scaledStroke.fY = SkScalarAbs(
1830 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001831
1832 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001833 bool isStrokeOnly =
1834 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001835 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1836
Brian Salomon05441c42017-05-15 16:45:49 -04001837 params.fInnerXRadius = 0;
1838 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001839 if (hasStroke) {
1840 if (SkScalarNearlyZero(scaledStroke.length())) {
1841 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1842 } else {
1843 scaledStroke.scale(SK_ScalarHalf);
1844 }
1845
1846 // we only handle thick strokes for near-circular ellipses
1847 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001848 (0.5f * params.fXRadius > params.fYRadius ||
1849 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850 return nullptr;
1851 }
1852
1853 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001854 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1855 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1856 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1857 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001858 return nullptr;
1859 }
1860
1861 // this is legit only if scale & translation (which should be the case at the moment)
1862 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001863 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1864 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001865 }
1866
Brian Salomon05441c42017-05-15 16:45:49 -04001867 params.fXRadius += scaledStroke.fX;
1868 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001870
1871 // For large ovals with low precision floats, we fall back to the path renderer.
1872 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1873 // minimum value to avoid divides by zero. With large ovals and low precision this
1874 // leads to blurring at the edge of the oval.
1875 const SkScalar kMaxOvalRadius = 16384;
1876 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1877 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1878 return nullptr;
1879 }
1880
Greg Daniel2655ede2019-04-10 00:49:28 +00001881 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001882 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001883 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001884
Herb Derbyc76d4092020-10-07 16:46:15 -04001885 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001886 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001887 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001888 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001889 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001890 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001891 SkStrokeRec::Style style = stroke.getStyle();
1892 bool isStrokeOnly =
1893 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001894
Brian Salomon05441c42017-05-15 16:45:49 -04001895 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1896 params.fInnerXRadius, params.fInnerYRadius,
1897 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1898 params.fCenter.fY - params.fYRadius,
1899 params.fCenter.fX + params.fXRadius,
1900 params.fCenter.fY + params.fYRadius)});
1901
Greg Daniel5faf4742019-10-01 15:14:44 -04001902 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001903
bsalomon4b4a7cc2016-07-08 04:42:54 -07001904 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001905 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001906
Brian Salomon05441c42017-05-15 16:45:49 -04001907 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1908 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001909 }
joshualitt76e7fb62015-02-11 08:52:27 -08001910
Brian Salomon289e3d82016-12-14 15:52:56 -05001911 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001912
Chris Dalton1706cbf2019-05-21 19:35:29 -06001913 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001914 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001915 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001916 } else {
1917 fHelper.visitProxies(func);
1918 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001919 }
1920
Chris Dalton6ce447a2019-06-23 18:07:38 -06001921 GrProcessorSet::Analysis finalize(
1922 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1923 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001924 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1925 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001926 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001927 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001928 GrProcessorAnalysisCoverage::kSingleChannel, color,
1929 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001930 }
1931
1932 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1933
bsalomone46f9fe2015-08-18 06:05:14 -07001934private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001935 GrProgramInfo* programInfo() override { return fProgramInfo; }
1936
Robert Phillips4133dc42020-03-11 15:55:55 -04001937 void onCreateProgramInfo(const GrCaps* caps,
1938 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001939 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001940 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001941 const GrXferProcessor::DstProxyView& dstProxyView,
1942 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001943 SkMatrix localMatrix;
1944 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001945 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001946 }
1947
Robert Phillips4490d922020-03-03 14:50:59 -05001948 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1949 fUseScale, localMatrix);
1950
Brian Salomon8afde5f2020-04-01 16:22:00 -04001951 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001952 dstProxyView, gp, GrPrimitiveType::kTriangles,
1953 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001954 }
1955
Robert Phillips4490d922020-03-03 14:50:59 -05001956 void onPrepareDraws(Target* target) override {
1957 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001958 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001959 if (!fProgramInfo) {
1960 return;
1961 }
1962 }
1963
1964 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001965 GrVertexWriter verts{helper.vertices()};
1966 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001967 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001968 return;
1969 }
1970
Brian Salomon05441c42017-05-15 16:45:49 -04001971 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001972 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001973 SkScalar xRadius = ellipse.fXRadius;
1974 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001975
1976 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001977 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1978 SkScalarInvert(xRadius),
1979 SkScalarInvert(yRadius),
1980 SkScalarInvert(ellipse.fInnerXRadius),
1981 SkScalarInvert(ellipse.fInnerYRadius)
1982 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001983 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1984 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001985
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001986 if (!fStroked) {
1987 // For filled ellipses we map a unit circle in the vertex attributes rather than
1988 // computing an ellipse and modifying that distance, so we normalize to 1
1989 xMaxOffset /= xRadius;
1990 yMaxOffset /= yRadius;
1991 }
1992
joshualitt76e7fb62015-02-11 08:52:27 -08001993 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001994 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1995 color,
1996 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05001997 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05001998 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001999 }
Robert Phillips4490d922020-03-03 14:50:59 -05002000 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002001 }
2002
2003 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002004 if (!fProgramInfo || !fMesh) {
2005 return;
2006 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002007
Chris Dalton765ed362020-03-16 17:34:44 -06002008 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2009 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2010 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002011 }
2012
Herb Derbye25c3002020-10-27 15:57:27 -04002013 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002014 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002015
Brian Salomon05441c42017-05-15 16:45:49 -04002016 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002017 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002018 }
2019
bsalomoncdaa97b2016-03-08 08:30:14 -08002020 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002021 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002022 }
2023
Brian Salomon05441c42017-05-15 16:45:49 -04002024 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002025 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2026 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002027 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002028 }
2029
Brian Salomon05441c42017-05-15 16:45:49 -04002030 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002031 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002032 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002033 }
2034
John Stilesaf366522020-08-13 09:57:34 -04002035#if GR_TEST_UTILS
2036 SkString onDumpInfo() const override {
2037 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2038 for (const auto& geo : fEllipses) {
2039 string.appendf(
2040 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2041 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2042 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2043 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2044 geo.fInnerXRadius, geo.fInnerYRadius);
2045 }
2046 string += fHelper.dumpInfo();
2047 return string;
2048 }
2049#endif
2050
Brian Salomon05441c42017-05-15 16:45:49 -04002051 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002052 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002053 SkScalar fXRadius;
2054 SkScalar fYRadius;
2055 SkScalar fInnerXRadius;
2056 SkScalar fInnerYRadius;
2057 SkRect fDevBounds;
2058 };
joshualitt76e7fb62015-02-11 08:52:27 -08002059
Brian Salomon289e3d82016-12-14 15:52:56 -05002060 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002061 Helper fHelper;
2062 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002063 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002064 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002065 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002066
Chris Daltoneb694b72020-03-16 09:25:50 -06002067 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002068 GrProgramInfo* fProgramInfo = nullptr;
2069
John Stiles7571f9e2020-09-02 22:42:33 -04002070 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002071};
2072
joshualitt76e7fb62015-02-11 08:52:27 -08002073/////////////////////////////////////////////////////////////////////////////////////////////////
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075class DIEllipseOp : public GrMeshDrawOp {
2076private:
2077 using Helper = GrSimpleMeshDrawOpHelper;
2078
2079 struct DeviceSpaceParams {
2080 SkPoint fCenter;
2081 SkScalar fXRadius;
2082 SkScalar fYRadius;
2083 SkScalar fInnerXRadius;
2084 SkScalar fInnerYRadius;
2085 DIEllipseStyle fStyle;
2086 };
2087
joshualitt76e7fb62015-02-11 08:52:27 -08002088public:
Brian Salomon25a88092016-12-01 09:36:50 -05002089 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002090
Herb Derbyc76d4092020-10-07 16:46:15 -04002091 static GrOp::Owner Make(GrRecordingContext* context,
2092 GrPaint&& paint,
2093 const SkMatrix& viewMatrix,
2094 const SkRect& ellipse,
2095 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002096 DeviceSpaceParams params;
2097 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2098 params.fXRadius = SkScalarHalf(ellipse.width());
2099 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002100
bsalomon4b4a7cc2016-07-08 04:42:54 -07002101 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002102 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2103 ? DIEllipseStyle::kStroke
2104 : (SkStrokeRec::kHairline_Style == style)
2105 ? DIEllipseStyle::kHairline
2106 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002107
Brian Salomon05441c42017-05-15 16:45:49 -04002108 params.fInnerXRadius = 0;
2109 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002110 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2111 SkScalar strokeWidth = stroke.getWidth();
2112
2113 if (SkScalarNearlyZero(strokeWidth)) {
2114 strokeWidth = SK_ScalarHalf;
2115 } else {
2116 strokeWidth *= SK_ScalarHalf;
2117 }
2118
2119 // we only handle thick strokes for near-circular ellipses
2120 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002121 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2122 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002123 return nullptr;
2124 }
2125
2126 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002127 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2128 (strokeWidth * strokeWidth) * params.fXRadius) {
2129 return nullptr;
2130 }
2131 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2132 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002133 return nullptr;
2134 }
2135
2136 // set inner radius (if needed)
2137 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002138 params.fInnerXRadius = params.fXRadius - strokeWidth;
2139 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002140 }
2141
Brian Salomon05441c42017-05-15 16:45:49 -04002142 params.fXRadius += strokeWidth;
2143 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002144 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002145
2146 // For large ovals with low precision floats, we fall back to the path renderer.
2147 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2148 // minimum value to avoid divides by zero. With large ovals and low precision this
2149 // leads to blurring at the edge of the oval.
2150 const SkScalar kMaxOvalRadius = 16384;
2151 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2152 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2153 return nullptr;
2154 }
2155
Brian Salomon05441c42017-05-15 16:45:49 -04002156 if (DIEllipseStyle::kStroke == params.fStyle &&
2157 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2158 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002159 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002160 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002161 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002162
Herb Derbyc76d4092020-10-07 16:46:15 -04002163 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002164 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002165 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002166 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002167 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002168 // This expands the outer rect so that after CTM we end up with a half-pixel border
2169 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2170 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2171 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2172 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2173 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2174 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002175
Brian Salomon05441c42017-05-15 16:45:49 -04002176 fEllipses.emplace_back(
2177 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2178 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2179 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2180 params.fCenter.fY - params.fYRadius - geoDy,
2181 params.fCenter.fX + params.fXRadius + geoDx,
2182 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002183 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002184 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002185 }
2186
Brian Salomon289e3d82016-12-14 15:52:56 -05002187 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002188
Chris Dalton1706cbf2019-05-21 19:35:29 -06002189 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002190 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002191 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002192 } else {
2193 fHelper.visitProxies(func);
2194 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002195 }
2196
Chris Dalton6ce447a2019-06-23 18:07:38 -06002197 GrProcessorSet::Analysis finalize(
2198 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2199 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002200 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2201 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002202 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002203 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002204 GrProcessorAnalysisCoverage::kSingleChannel, color,
2205 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002206 }
2207
2208 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2209
bsalomone46f9fe2015-08-18 06:05:14 -07002210private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002211 GrProgramInfo* programInfo() override { return fProgramInfo; }
2212
Robert Phillips4133dc42020-03-11 15:55:55 -04002213 void onCreateProgramInfo(const GrCaps* caps,
2214 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002215 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002216 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002217 const GrXferProcessor::DstProxyView& dstProxyView,
2218 GrXferBarrierFlags renderPassXferBarriers) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002219 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2220 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002221 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002222
Brian Salomon8afde5f2020-04-01 16:22:00 -04002223 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002224 dstProxyView, gp, GrPrimitiveType::kTriangles,
2225 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002226 }
2227
Robert Phillips4490d922020-03-03 14:50:59 -05002228 void onPrepareDraws(Target* target) override {
2229 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002230 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002231 }
2232
2233 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002234 GrVertexWriter verts{helper.vertices()};
2235 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002236 return;
2237 }
2238
Brian Salomon05441c42017-05-15 16:45:49 -04002239 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002240 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002241 SkScalar xRadius = ellipse.fXRadius;
2242 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002243
joshualitt76e7fb62015-02-11 08:52:27 -08002244 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002245 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2246 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002247
Brian Osman9d958b52018-11-13 12:46:56 -05002248 // By default, constructed so that inner offset is (0, 0) for all points
2249 SkScalar innerRatioX = -offsetDx;
2250 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002251
Brian Osman9d958b52018-11-13 12:46:56 -05002252 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002253 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002254 innerRatioX = xRadius / ellipse.fInnerXRadius;
2255 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002256 }
joshualitt76e7fb62015-02-11 08:52:27 -08002257
Brian Osman2b6e3902018-11-21 15:29:43 -05002258 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2259 color,
2260 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002261 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002262 origin_centered_tri_strip(innerRatioX + offsetDx,
2263 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002264 }
Robert Phillips4490d922020-03-03 14:50:59 -05002265 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002266 }
2267
2268 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002269 if (!fProgramInfo || !fMesh) {
2270 return;
2271 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002272
Chris Dalton765ed362020-03-16 17:34:44 -06002273 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2274 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2275 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002276 }
halcanary9d524f22016-03-29 09:03:52 -07002277
Herb Derbye25c3002020-10-27 15:57:27 -04002278 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002279 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002280 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002281 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002282 }
2283
bsalomoncdaa97b2016-03-08 08:30:14 -08002284 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002285 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002286 }
2287
joshualittd96a67b2015-05-05 14:09:05 -07002288 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002289 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002290 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002291 }
2292
Brian Salomon05441c42017-05-15 16:45:49 -04002293 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002294 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002295 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002296 }
2297
John Stilesaf366522020-08-13 09:57:34 -04002298#if GR_TEST_UTILS
2299 SkString onDumpInfo() const override {
2300 SkString string;
2301 for (const auto& geo : fEllipses) {
2302 string.appendf(
2303 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2304 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2305 "GeoDY: %.2f\n",
2306 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2307 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2308 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2309 }
2310 string += fHelper.dumpInfo();
2311 return string;
2312 }
2313#endif
2314
Brian Salomon05441c42017-05-15 16:45:49 -04002315 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2316 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002317
Brian Salomon05441c42017-05-15 16:45:49 -04002318 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002319 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002320 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002321 SkScalar fXRadius;
2322 SkScalar fYRadius;
2323 SkScalar fInnerXRadius;
2324 SkScalar fInnerYRadius;
2325 SkScalar fGeoDx;
2326 SkScalar fGeoDy;
2327 DIEllipseStyle fStyle;
2328 SkRect fBounds;
2329 };
2330
Brian Salomon05441c42017-05-15 16:45:49 -04002331 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002332 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002333 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002334 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002335
Chris Daltoneb694b72020-03-16 09:25:50 -06002336 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002337 GrProgramInfo* fProgramInfo = nullptr;
2338
John Stiles7571f9e2020-09-02 22:42:33 -04002339 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002340};
2341
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002342///////////////////////////////////////////////////////////////////////////////
2343
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002344// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002345//
2346// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2347// ____________
2348// |_|________|_|
2349// | | | |
2350// | | | |
2351// | | | |
2352// |_|________|_|
2353// |_|________|_|
2354//
2355// For strokes, we don't draw the center quad.
2356//
2357// For circular roundrects, in the case where the stroke width is greater than twice
2358// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002359// in the center. The shared vertices are duplicated so we can set a different outer radius
2360// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002361// ____________
2362// |_|________|_|
2363// | |\ ____ /| |
2364// | | | | | |
2365// | | |____| | |
2366// |_|/______\|_|
2367// |_|________|_|
2368//
2369// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002370//
2371// For filled rrects that need to provide a distance vector we resuse the overstroke
2372// geometry but make the inner rect degenerate (either a point or a horizontal or
2373// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002374
jvanverth84839f62016-08-29 10:16:40 -07002375static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002376 // clang-format off
2377 // overstroke quads
2378 // we place this at the beginning so that we can skip these indices when rendering normally
2379 16, 17, 19, 16, 19, 18,
2380 19, 17, 23, 19, 23, 21,
2381 21, 23, 22, 21, 22, 20,
2382 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002383
Brian Salomon289e3d82016-12-14 15:52:56 -05002384 // corners
2385 0, 1, 5, 0, 5, 4,
2386 2, 3, 7, 2, 7, 6,
2387 8, 9, 13, 8, 13, 12,
2388 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002389
Brian Salomon289e3d82016-12-14 15:52:56 -05002390 // edges
2391 1, 2, 6, 1, 6, 5,
2392 4, 5, 9, 4, 9, 8,
2393 6, 7, 11, 6, 11, 10,
2394 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002395
Brian Salomon289e3d82016-12-14 15:52:56 -05002396 // center
2397 // we place this at the end so that we can ignore these indices when not rendering as filled
2398 5, 6, 10, 5, 10, 9,
2399 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002400};
Brian Salomon289e3d82016-12-14 15:52:56 -05002401
jvanverth84839f62016-08-29 10:16:40 -07002402// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002403static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002404
jvanverth84839f62016-08-29 10:16:40 -07002405// overstroke count is arraysize minus the center indices
2406static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2407// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002408static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002409// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002410static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2411static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002412static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002413
jvanverthc3d0e422016-08-25 08:12:35 -07002414enum RRectType {
2415 kFill_RRectType,
2416 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002417 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002418};
2419
jvanverth84839f62016-08-29 10:16:40 -07002420static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002421 switch (type) {
2422 case kFill_RRectType:
2423 case kStroke_RRectType:
2424 return kVertsPerStandardRRect;
2425 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002426 return kVertsPerOverstrokeRRect;
2427 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002428 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002429}
2430
2431static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002432 switch (type) {
2433 case kFill_RRectType:
2434 return kIndicesPerFillRRect;
2435 case kStroke_RRectType:
2436 return kIndicesPerStrokeRRect;
2437 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002438 return kIndicesPerOverstrokeRRect;
2439 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002440 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002441}
2442
2443static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002444 switch (type) {
2445 case kFill_RRectType:
2446 case kStroke_RRectType:
2447 return gStandardRRectIndices;
2448 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002449 return gOverstrokeRRectIndices;
2450 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002451 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002452}
2453
joshualitt76e7fb62015-02-11 08:52:27 -08002454///////////////////////////////////////////////////////////////////////////////////////////////////
2455
Robert Phillips79839d42016-10-06 15:03:34 -04002456// For distance computations in the interior of filled rrects we:
2457//
2458// add a interior degenerate (point or line) rect
2459// each vertex of that rect gets -outerRad as its radius
2460// this makes the computation of the distance to the outer edge be negative
2461// negative values are caught and then handled differently in the GP's onEmitCode
2462// each vertex is also given the normalized x & y distance from the interior rect's edge
2463// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2464
Brian Salomon05441c42017-05-15 16:45:49 -04002465class CircularRRectOp : public GrMeshDrawOp {
2466private:
2467 using Helper = GrSimpleMeshDrawOpHelper;
2468
joshualitt76e7fb62015-02-11 08:52:27 -08002469public:
Brian Salomon25a88092016-12-01 09:36:50 -05002470 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002471
bsalomon4b4a7cc2016-07-08 04:42:54 -07002472 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2473 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002474 static GrOp::Owner Make(GrRecordingContext* context,
2475 GrPaint&& paint,
2476 const SkMatrix& viewMatrix,
2477 const SkRect& devRect,
2478 float devRadius,
2479 float devStrokeWidth,
2480 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002481 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002482 devRect, devRadius,
2483 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002484 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002485 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002486 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2487 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002488 : INHERITED(ClassID())
2489 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002490 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002491 SkRect bounds = devRect;
2492 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2493 SkScalar innerRadius = 0.0f;
2494 SkScalar outerRadius = devRadius;
2495 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002496 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002497 if (devStrokeWidth > 0) {
2498 if (SkScalarNearlyZero(devStrokeWidth)) {
2499 halfWidth = SK_ScalarHalf;
2500 } else {
2501 halfWidth = SkScalarHalf(devStrokeWidth);
2502 }
joshualitt76e7fb62015-02-11 08:52:27 -08002503
bsalomon4b4a7cc2016-07-08 04:42:54 -07002504 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002505 // Outset stroke by 1/4 pixel
2506 devStrokeWidth += 0.25f;
2507 // If stroke is greater than width or height, this is still a fill
2508 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002509 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002510 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002511 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002512 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002513 }
2514 outerRadius += halfWidth;
2515 bounds.outset(halfWidth, halfWidth);
2516 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002517
Greg Daniel2655ede2019-04-10 00:49:28 +00002518 // The radii are outset for two reasons. First, it allows the shader to simply perform
2519 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2520 // Second, the outer radius is used to compute the verts of the bounding box that is
2521 // rendered and the outset ensures the box will cover all partially covered by the rrect
2522 // corners.
2523 outerRadius += SK_ScalarHalf;
2524 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002525
Greg Daniel5faf4742019-10-01 15:14:44 -04002526 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002527
Greg Daniel2655ede2019-04-10 00:49:28 +00002528 // Expand the rect for aa to generate correct vertices.
2529 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002530
Brian Salomon05441c42017-05-15 16:45:49 -04002531 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002532 fVertCount = rrect_type_to_vert_count(type);
2533 fIndexCount = rrect_type_to_index_count(type);
2534 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002535 }
2536
Brian Salomon289e3d82016-12-14 15:52:56 -05002537 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002538
Chris Dalton1706cbf2019-05-21 19:35:29 -06002539 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002540 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002541 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002542 } else {
2543 fHelper.visitProxies(func);
2544 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002545 }
2546
Chris Dalton6ce447a2019-06-23 18:07:38 -06002547 GrProcessorSet::Analysis finalize(
2548 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2549 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002550 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002551 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002552 GrProcessorAnalysisCoverage::kSingleChannel, color,
2553 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002554 }
2555
2556 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2557
Brian Salomon92aee3d2016-12-21 09:20:25 -05002558private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002559 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002560 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002561 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002562 SkASSERT(smInset < bigInset);
2563
2564 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002565 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2566 color,
2567 xOffset, 0.0f,
2568 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002569
2570 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002571 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2572 color,
2573 xOffset, 0.0f,
2574 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002575
Brian Osmana1d4eb92018-12-06 16:33:10 -05002576 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2577 color,
2578 0.0f, 0.0f,
2579 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002580
Brian Osmana1d4eb92018-12-06 16:33:10 -05002581 verts.write(bounds.fRight - 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.fLeft + bigInset, bounds.fBottom - 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.fRight - bigInset, bounds.fBottom - bigInset,
2592 color,
2593 0.0f, 0.0f,
2594 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002595
2596 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002597 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2598 color,
2599 xOffset, 0.0f,
2600 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002601
2602 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002603 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2604 color,
2605 xOffset, 0.0f,
2606 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002607 }
2608
Robert Phillips2669a7b2020-03-12 12:07:19 -04002609 GrProgramInfo* programInfo() override { return fProgramInfo; }
2610
Robert Phillips4133dc42020-03-11 15:55:55 -04002611 void onCreateProgramInfo(const GrCaps* caps,
2612 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002613 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002614 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002615 const GrXferProcessor::DstProxyView& dstProxyView,
2616 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002617 // Invert the view matrix as a local matrix (if any other processors require coords).
2618 SkMatrix localMatrix;
2619 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002620 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002621 }
2622
Robert Phillips4490d922020-03-03 14:50:59 -05002623 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002624 false, false, false, false,
2625 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002626
Brian Salomon8afde5f2020-04-01 16:22:00 -04002627 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002628 dstProxyView, gp, GrPrimitiveType::kTriangles,
2629 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002630 }
2631
Robert Phillips4490d922020-03-03 14:50:59 -05002632 void onPrepareDraws(Target* target) override {
2633 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002634 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002635 if (!fProgramInfo) {
2636 return;
2637 }
2638 }
2639
Brian Salomon12d22642019-01-29 14:38:50 -05002640 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002641 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002642
Robert Phillips4490d922020-03-03 14:50:59 -05002643 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2644 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002645 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002646 SkDebugf("Could not allocate vertices\n");
2647 return;
2648 }
2649
Brian Salomon12d22642019-01-29 14:38:50 -05002650 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002651 int firstIndex = 0;
2652 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2653 if (!indices) {
2654 SkDebugf("Could not allocate indices\n");
2655 return;
2656 }
2657
2658 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002659 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002660 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002661 SkScalar outerRadius = rrect.fOuterRadius;
2662 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002663
Brian Salomon289e3d82016-12-14 15:52:56 -05002664 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2665 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002666
Brian Salomon289e3d82016-12-14 15:52:56 -05002667 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002668 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002669 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002670 SkScalar innerRadius = rrect.fType != kFill_RRectType
2671 ? rrect.fInnerRadius / rrect.fOuterRadius
2672 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002673 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002674 verts.write(bounds.fLeft, yCoords[i],
2675 color,
2676 -1.0f, yOuterRadii[i],
2677 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002678
Brian Osmana1d4eb92018-12-06 16:33:10 -05002679 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2680 color,
2681 0.0f, yOuterRadii[i],
2682 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002683
Brian Osmana1d4eb92018-12-06 16:33:10 -05002684 verts.write(bounds.fRight - 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, yCoords[i],
2690 color,
2691 1.0f, yOuterRadii[i],
2692 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002693 }
jvanverthc3d0e422016-08-25 08:12:35 -07002694 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002695 // Effectively this is an additional stroked rrect, with its
2696 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2697 // This will give us correct AA in the center and the correct
2698 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002699 //
jvanvertha4f1af82016-08-29 07:17:47 -07002700 // Also, the outer offset is a constant vector pointing to the right, which
2701 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002702 if (kOverstroke_RRectType == rrect.fType) {
2703 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002704
Brian Salomon05441c42017-05-15 16:45:49 -04002705 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002706 // this is the normalized distance from the outer rectangle of this
2707 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002708 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002709
Brian Osmana1d4eb92018-12-06 16:33:10 -05002710 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002711 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002712 }
jvanverth6a397612016-08-26 08:15:33 -07002713
Brian Salomon05441c42017-05-15 16:45:49 -04002714 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2715 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002716 for (int i = 0; i < primIndexCount; ++i) {
2717 *indices++ = primIndices[i] + currStartVertex;
2718 }
2719
Brian Salomon05441c42017-05-15 16:45:49 -04002720 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002721 }
2722
Robert Phillips4490d922020-03-03 14:50:59 -05002723 fMesh = target->allocMesh();
2724 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002725 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002726 }
2727
2728 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002729 if (!fProgramInfo || !fMesh) {
2730 return;
2731 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002732
Chris Dalton765ed362020-03-16 17:34:44 -06002733 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2734 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2735 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002736 }
2737
Herb Derbye25c3002020-10-27 15:57:27 -04002738 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002739 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002740
2741 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002742 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002743 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002744 }
2745
Brian Salomon05441c42017-05-15 16:45:49 -04002746 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002747 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002748 }
2749
Brian Salomon05441c42017-05-15 16:45:49 -04002750 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002751 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2752 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002753 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002754 }
2755
Brian Salomon05441c42017-05-15 16:45:49 -04002756 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002757 fVertCount += that->fVertCount;
2758 fIndexCount += that->fIndexCount;
2759 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002760 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002761 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002762 }
2763
John Stilesaf366522020-08-13 09:57:34 -04002764#if GR_TEST_UTILS
2765 SkString onDumpInfo() const override {
2766 SkString string;
2767 for (int i = 0; i < fRRects.count(); ++i) {
2768 string.appendf(
2769 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2770 "InnerRad: %.2f, OuterRad: %.2f\n",
2771 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2772 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2773 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2774 fRRects[i].fOuterRadius);
2775 }
2776 string += fHelper.dumpInfo();
2777 return string;
2778 }
2779#endif
2780
Brian Salomon05441c42017-05-15 16:45:49 -04002781 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002782 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002783 SkScalar fInnerRadius;
2784 SkScalar fOuterRadius;
2785 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002786 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002787 };
2788
Brian Salomon289e3d82016-12-14 15:52:56 -05002789 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002790 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002791 int fVertCount;
2792 int fIndexCount;
2793 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002794 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002795 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002796
Chris Daltoneb694b72020-03-16 09:25:50 -06002797 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002798 GrProgramInfo* fProgramInfo = nullptr;
2799
John Stiles7571f9e2020-09-02 22:42:33 -04002800 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002801};
2802
jvanverth84839f62016-08-29 10:16:40 -07002803static const int kNumRRectsInIndexBuffer = 256;
2804
2805GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2806GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002807static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2808 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002809 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2810 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2811 switch (type) {
2812 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002813 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002814 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2815 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002816 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002817 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002818 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2819 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002820 default:
2821 SkASSERT(false);
2822 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002823 }
jvanverth84839f62016-08-29 10:16:40 -07002824}
2825
Brian Salomon05441c42017-05-15 16:45:49 -04002826class EllipticalRRectOp : public GrMeshDrawOp {
2827private:
2828 using Helper = GrSimpleMeshDrawOpHelper;
2829
joshualitt76e7fb62015-02-11 08:52:27 -08002830public:
Brian Salomon25a88092016-12-01 09:36:50 -05002831 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002832
bsalomon4b4a7cc2016-07-08 04:42:54 -07002833 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2834 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002835 static GrOp::Owner Make(GrRecordingContext* context,
2836 GrPaint&& paint,
2837 const SkMatrix& viewMatrix,
2838 const SkRect& devRect,
2839 float devXRadius,
2840 float devYRadius,
2841 SkVector devStrokeWidths,
2842 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002843 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2844 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002845 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2846 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002847 if (devStrokeWidths.fX > 0) {
2848 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2849 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2850 } else {
2851 devStrokeWidths.scale(SK_ScalarHalf);
2852 }
joshualitt76e7fb62015-02-11 08:52:27 -08002853
bsalomon4b4a7cc2016-07-08 04:42:54 -07002854 // we only handle thick strokes for near-circular ellipses
2855 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002856 (SK_ScalarHalf * devXRadius > devYRadius ||
2857 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002858 return nullptr;
2859 }
2860
2861 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002862 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2863 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002864 return nullptr;
2865 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002866 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2867 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002868 return nullptr;
2869 }
Brian Salomon05441c42017-05-15 16:45:49 -04002870 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002871 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002872 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002873 devXRadius, devYRadius, devStrokeWidths,
2874 strokeOnly);
2875 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002876
Herb Derbyc76d4092020-10-07 16:46:15 -04002877 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002878 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2879 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002880 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002881 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002882 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002883 SkScalar innerXRadius = 0.0f;
2884 SkScalar innerYRadius = 0.0f;
2885 SkRect bounds = devRect;
2886 bool stroked = false;
2887 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002888 // this is legit only if scale & translation (which should be the case at the moment)
2889 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002890 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2891 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002892 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2893 }
2894
Brian Salomon05441c42017-05-15 16:45:49 -04002895 devXRadius += devStrokeHalfWidths.fX;
2896 devYRadius += devStrokeHalfWidths.fY;
2897 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002898 }
2899
Brian Salomon05441c42017-05-15 16:45:49 -04002900 fStroked = stroked;
2901 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002902 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002903 // Expand the rect for aa in order to generate the correct vertices.
2904 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002905 fRRects.emplace_back(
2906 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002907 }
2908
Brian Salomon289e3d82016-12-14 15:52:56 -05002909 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002910
Chris Dalton1706cbf2019-05-21 19:35:29 -06002911 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002912 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002913 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002914 } else {
2915 fHelper.visitProxies(func);
2916 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002917 }
2918
Chris Dalton6ce447a2019-06-23 18:07:38 -06002919 GrProcessorSet::Analysis finalize(
2920 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2921 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002922 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002923 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002924 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002925 GrProcessorAnalysisCoverage::kSingleChannel, color,
2926 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002927 }
2928
2929 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2930
bsalomone46f9fe2015-08-18 06:05:14 -07002931private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002932 GrProgramInfo* programInfo() override { return fProgramInfo; }
2933
Robert Phillips4133dc42020-03-11 15:55:55 -04002934 void onCreateProgramInfo(const GrCaps* caps,
2935 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002936 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002937 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002938 const GrXferProcessor::DstProxyView& dstProxyView,
2939 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002940 SkMatrix localMatrix;
2941 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002942 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002943 }
2944
Robert Phillips4490d922020-03-03 14:50:59 -05002945 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2946 fUseScale, localMatrix);
2947
Brian Salomon8afde5f2020-04-01 16:22:00 -04002948 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002949 dstProxyView, gp, GrPrimitiveType::kTriangles,
2950 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002951 }
2952
Robert Phillips4490d922020-03-03 14:50:59 -05002953 void onPrepareDraws(Target* target) override {
2954 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002955 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002956 if (!fProgramInfo) {
2957 return;
2958 }
2959 }
joshualitt76e7fb62015-02-11 08:52:27 -08002960
bsalomonb5238a72015-05-05 07:49:49 -07002961 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002962 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002963 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2964 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002965
Brian Salomon12d22642019-01-29 14:38:50 -05002966 if (!indexBuffer) {
2967 SkDebugf("Could not allocate indices\n");
2968 return;
2969 }
Robert Phillips4490d922020-03-03 14:50:59 -05002970 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2971 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002972 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002973 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002974 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002975 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002976 SkDebugf("Could not allocate vertices\n");
2977 return;
2978 }
2979
Brian Salomon05441c42017-05-15 16:45:49 -04002980 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002981 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002982 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002983 float reciprocalRadii[4] = {
2984 SkScalarInvert(rrect.fXRadius),
2985 SkScalarInvert(rrect.fYRadius),
2986 SkScalarInvert(rrect.fInnerXRadius),
2987 SkScalarInvert(rrect.fInnerYRadius)
2988 };
joshualitt76e7fb62015-02-11 08:52:27 -08002989
Brian Osmane3afdd52020-10-28 10:49:56 -04002990 // If the stroke width is exactly double the radius, the inner radii will be zero.
2991 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
2992 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
2993 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
2994
joshualitt76e7fb62015-02-11 08:52:27 -08002995 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00002996 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2997 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002998
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002999 SkScalar xMaxOffset = xOuterRadius;
3000 SkScalar yMaxOffset = yOuterRadius;
3001 if (!fStroked) {
3002 // For filled rrects we map a unit circle in the vertex attributes rather than
3003 // computing an ellipse and modifying that distance, so we normalize to 1.
3004 xMaxOffset /= rrect.fXRadius;
3005 yMaxOffset /= rrect.fYRadius;
3006 }
3007
Brian Salomon05441c42017-05-15 16:45:49 -04003008 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003009
Brian Salomon289e3d82016-12-14 15:52:56 -05003010 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3011 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003012 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003013 SK_ScalarNearlyZero, // we're using inversesqrt() in
3014 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003015 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003016
Brian Osman788b9162020-02-07 10:36:46 -05003017 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003018 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003019 verts.write(bounds.fLeft, yCoords[i],
3020 color,
3021 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003022 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003023 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003024
Brian Osmana1d4eb92018-12-06 16:33:10 -05003025 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3026 color,
3027 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003028 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003029 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003030
Brian Osmana1d4eb92018-12-06 16:33:10 -05003031 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3032 color,
3033 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003034 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003035 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003036
Brian Osmana1d4eb92018-12-06 16:33:10 -05003037 verts.write(bounds.fRight, yCoords[i],
3038 color,
3039 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003040 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003041 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003042 }
3043 }
Robert Phillips4490d922020-03-03 14:50:59 -05003044 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003045 }
3046
3047 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003048 if (!fProgramInfo || !fMesh) {
3049 return;
3050 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003051
Chris Dalton765ed362020-03-16 17:34:44 -06003052 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3053 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3054 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003055 }
3056
Herb Derbye25c3002020-10-27 15:57:27 -04003057 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003058 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003059
Brian Salomon05441c42017-05-15 16:45:49 -04003060 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003061 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003062 }
3063
bsalomoncdaa97b2016-03-08 08:30:14 -08003064 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003065 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003066 }
3067
Brian Salomon05441c42017-05-15 16:45:49 -04003068 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003069 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3070 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003071 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003072 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003073
Brian Salomon05441c42017-05-15 16:45:49 -04003074 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003075 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003076 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003077 }
3078
John Stilesaf366522020-08-13 09:57:34 -04003079#if GR_TEST_UTILS
3080 SkString onDumpInfo() const override {
3081 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3082 for (const auto& geo : fRRects) {
3083 string.appendf(
3084 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3085 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3086 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3087 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3088 geo.fInnerXRadius, geo.fInnerYRadius);
3089 }
3090 string += fHelper.dumpInfo();
3091 return string;
3092 }
3093#endif
3094
Brian Salomon05441c42017-05-15 16:45:49 -04003095 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003096 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003097 SkScalar fXRadius;
3098 SkScalar fYRadius;
3099 SkScalar fInnerXRadius;
3100 SkScalar fInnerYRadius;
3101 SkRect fDevBounds;
3102 };
3103
Brian Salomon289e3d82016-12-14 15:52:56 -05003104 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003105 Helper fHelper;
3106 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003107 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003108 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003109 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003110
Chris Daltoneb694b72020-03-16 09:25:50 -06003111 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003112 GrProgramInfo* fProgramInfo = nullptr;
3113
John Stiles7571f9e2020-09-02 22:42:33 -04003114 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003115};
3116
Herb Derbyc76d4092020-10-07 16:46:15 -04003117GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3118 GrPaint&& paint,
3119 const SkMatrix& viewMatrix,
3120 const SkRRect& rrect,
3121 const SkStrokeRec& stroke,
3122 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003123 SkASSERT(viewMatrix.rectStaysRect());
3124 SkASSERT(viewMatrix.isSimilarity());
3125 SkASSERT(rrect.isSimple());
3126 SkASSERT(!rrect.isOval());
3127 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3128
3129 // RRect ops only handle simple, but not too simple, rrects.
3130 // Do any matrix crunching before we reset the draw state for device coords.
3131 const SkRect& rrectBounds = rrect.getBounds();
3132 SkRect bounds;
3133 viewMatrix.mapRect(&bounds, rrectBounds);
3134
3135 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3136 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3137 viewMatrix[SkMatrix::kMSkewY]));
3138
3139 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3140 SkScalar scaledStroke = -1;
3141 SkScalar strokeWidth = stroke.getWidth();
3142 SkStrokeRec::Style style = stroke.getStyle();
3143
3144 bool isStrokeOnly =
3145 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3146 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3147
3148 if (hasStroke) {
3149 if (SkStrokeRec::kHairline_Style == style) {
3150 scaledStroke = SK_Scalar1;
3151 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003152 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3153 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003154 }
3155 }
3156
3157 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3158 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3159 // patch will have fractional coverage. This only matters when the interior is actually filled.
3160 // We could consider falling back to rect rendering here, since a tiny radius is
3161 // indistinguishable from a square corner.
3162 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3163 return nullptr;
3164 }
3165
3166 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3167 scaledStroke, isStrokeOnly);
3168}
3169
Herb Derbyc76d4092020-10-07 16:46:15 -04003170GrOp::Owner make_rrect_op(GrRecordingContext* context,
3171 GrPaint&& paint,
3172 const SkMatrix& viewMatrix,
3173 const SkRRect& rrect,
3174 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003175 SkASSERT(viewMatrix.rectStaysRect());
3176 SkASSERT(rrect.isSimple());
3177 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003178
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003179 // RRect ops only handle simple, but not too simple, rrects.
3180 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003181 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003182 SkRect bounds;
3183 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003184
Mike Reed242135a2018-02-22 13:41:39 -05003185 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003186 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3187 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3188 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3189 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003190
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003191 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003192
bsalomon4b4a7cc2016-07-08 04:42:54 -07003193 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3194 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003195 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003196
Brian Salomon289e3d82016-12-14 15:52:56 -05003197 bool isStrokeOnly =
3198 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003199 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3200
3201 if (hasStroke) {
3202 if (SkStrokeRec::kHairline_Style == style) {
3203 scaledStroke.set(1, 1);
3204 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003205 scaledStroke.fX = SkScalarAbs(
3206 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3207 scaledStroke.fY = SkScalarAbs(
3208 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003209 }
3210
Jim Van Verth64b85892019-06-17 12:01:46 -04003211 // if half of strokewidth is greater than radius, we don't handle that right now
3212 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3213 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003214 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003215 }
3216 }
3217
Brian Salomon8a97f562019-04-18 14:07:27 -04003218 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003219 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003220 std::swap(xRadius, yRadius);
3221 std::swap(scaledStroke.fX, scaledStroke.fY);
3222 }
3223
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003224 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3225 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3226 // patch will have fractional coverage. This only matters when the interior is actually filled.
3227 // We could consider falling back to rect rendering here, since a tiny radius is
3228 // indistinguishable from a square corner.
3229 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003230 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003231 }
3232
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003233 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003234 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3235 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003236}
3237
Herb Derbyc76d4092020-10-07 16:46:15 -04003238GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3239 GrPaint&& paint,
3240 const SkMatrix& viewMatrix,
3241 const SkRRect& rrect,
3242 const SkStrokeRec& stroke,
3243 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003244 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003245 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003246 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003247 }
3248
3249 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003250 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003251 }
3252
Greg Daniel2655ede2019-04-10 00:49:28 +00003253 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003254}
joshualitt3e708c52015-04-30 13:49:27 -07003255
bsalomon4b4a7cc2016-07-08 04:42:54 -07003256///////////////////////////////////////////////////////////////////////////////
3257
Herb Derbyc76d4092020-10-07 16:46:15 -04003258GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3259 GrPaint&& paint,
3260 const SkMatrix& viewMatrix,
3261 const SkRect& oval,
3262 const GrStyle& style,
3263 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003264 SkScalar width = oval.width();
3265 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3266 circle_stays_circle(viewMatrix));
3267
3268 auto r = width / 2.f;
3269 SkPoint center = { oval.centerX(), oval.centerY() };
3270 if (style.hasNonDashPathEffect()) {
3271 return nullptr;
3272 } else if (style.isDashed()) {
3273 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3274 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3275 return nullptr;
3276 }
3277 auto onInterval = style.dashIntervals()[0];
3278 auto offInterval = style.dashIntervals()[1];
3279 if (offInterval == 0) {
3280 GrStyle strokeStyle(style.strokeRec(), nullptr);
3281 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3282 strokeStyle, shaderCaps);
3283 } else if (onInterval == 0) {
3284 // There is nothing to draw but we have no way to indicate that here.
3285 return nullptr;
3286 }
3287 auto angularOnInterval = onInterval / r;
3288 auto angularOffInterval = offInterval / r;
3289 auto phaseAngle = style.dashPhase() / r;
3290 // Currently this function doesn't accept ovals with different start angles, though
3291 // it could.
3292 static const SkScalar kStartAngle = 0.f;
3293 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3294 style.strokeRec().getWidth(), kStartAngle,
3295 angularOnInterval, angularOffInterval, phaseAngle);
3296 }
3297 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3298}
3299
Herb Derbyc76d4092020-10-07 16:46:15 -04003300GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3301 GrPaint&& paint,
3302 const SkMatrix& viewMatrix,
3303 const SkRect& oval,
3304 const GrStyle& style,
3305 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003306 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003307 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003308 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3309 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003310 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003311 }
3312
3313 if (style.pathEffect()) {
3314 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003315 }
3316
Stan Ilieveb868aa2017-02-21 11:06:16 -05003317 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003318 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003319 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003320 }
3321
Stan Ilieveb868aa2017-02-21 11:06:16 -05003322 // Otherwise, if we have shader derivative support, render as device-independent
3323 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003324 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3325 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3326 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3327 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3328 // Check for near-degenerate matrix
3329 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003330 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003331 style.strokeRec());
3332 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003333 }
3334
bsalomon4b4a7cc2016-07-08 04:42:54 -07003335 return nullptr;
3336}
3337
3338///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003339
Herb Derbyc76d4092020-10-07 16:46:15 -04003340GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3341 GrPaint&& paint,
3342 const SkMatrix& viewMatrix,
3343 const SkRect& oval, SkScalar startAngle,
3344 SkScalar sweepAngle, bool useCenter,
3345 const GrStyle& style,
3346 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003347 SkASSERT(!oval.isEmpty());
3348 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003349 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003350 if (SkScalarAbs(sweepAngle) >= 360.f) {
3351 return nullptr;
3352 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003353 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3354 return nullptr;
3355 }
3356 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003357 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3358 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003359 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003360 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003361}
3362
3363///////////////////////////////////////////////////////////////////////////////
3364
Hal Canary6f6961e2017-01-31 13:50:44 -05003365#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003366
Brian Salomon05441c42017-05-15 16:45:49 -04003367GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003368 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003369 SkScalar rotate = random->nextSScalar1() * 360.f;
3370 SkScalar translateX = random->nextSScalar1() * 1000.f;
3371 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003372 SkScalar scale;
3373 do {
3374 scale = random->nextSScalar1() * 100.f;
3375 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003376 SkMatrix viewMatrix;
3377 viewMatrix.setRotate(rotate);
3378 viewMatrix.postTranslate(translateX, translateY);
3379 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003380 SkRect circle = GrTest::TestSquare(random);
3381 SkPoint center = {circle.centerX(), circle.centerY()};
3382 SkScalar radius = circle.width() / 2.f;
3383 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003384 CircleOp::ArcParams arcParamsTmp;
3385 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003386 if (random->nextBool()) {
3387 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003388 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3389 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003390 arcParams = &arcParamsTmp;
3391 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003392 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3393 center, radius,
3394 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003395 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003396 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003397 }
Mike Klein16885072018-12-11 09:54:31 -05003398 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003399 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003400}
3401
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003402GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3403 SkScalar rotate = random->nextSScalar1() * 360.f;
3404 SkScalar translateX = random->nextSScalar1() * 1000.f;
3405 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003406 SkScalar scale;
3407 do {
3408 scale = random->nextSScalar1() * 100.f;
3409 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003410 SkMatrix viewMatrix;
3411 viewMatrix.setRotate(rotate);
3412 viewMatrix.postTranslate(translateX, translateY);
3413 viewMatrix.postScale(scale, scale);
3414 SkRect circle = GrTest::TestSquare(random);
3415 SkPoint center = {circle.centerX(), circle.centerY()};
3416 SkScalar radius = circle.width() / 2.f;
3417 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3418 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3419 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3420 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3421 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003422 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3423 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003424 startAngle, onAngle, offAngle, phase);
3425}
3426
Brian Salomon05441c42017-05-15 16:45:49 -04003427GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003428 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003429 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003430 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003431 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003432}
3433
Brian Salomon05441c42017-05-15 16:45:49 -04003434GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003435 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003436 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003437 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003438 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003439}
3440
Jim Van Verth64b85892019-06-17 12:01:46 -04003441GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3442 do {
3443 SkScalar rotate = random->nextSScalar1() * 360.f;
3444 SkScalar translateX = random->nextSScalar1() * 1000.f;
3445 SkScalar translateY = random->nextSScalar1() * 1000.f;
3446 SkScalar scale;
3447 do {
3448 scale = random->nextSScalar1() * 100.f;
3449 } while (scale == 0);
3450 SkMatrix viewMatrix;
3451 viewMatrix.setRotate(rotate);
3452 viewMatrix.postTranslate(translateX, translateY);
3453 viewMatrix.postScale(scale, scale);
3454 SkRect rect = GrTest::TestRect(random);
3455 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3456 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3457 if (rrect.isOval()) {
3458 continue;
3459 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003460 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003461 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3462 GrTest::TestStrokeRec(random), nullptr);
3463 if (op) {
3464 return op;
3465 }
3466 assert_alive(paint);
3467 } while (true);
3468}
3469
Brian Salomon05441c42017-05-15 16:45:49 -04003470GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003471 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003472 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003473 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003474 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003475}
3476
3477#endif