blob: fa0d983061baf811aec037a1828931062ff37873 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -060070 return arena->make([&](void* ptr) {
71 return new (ptr) CircleGeometryProcessor(stroke, clipPlane, isectPlane, unionPlane,
72 roundCaps, wideColor, localMatrix);
73 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050074 }
75
76 const char* name() const override { return "CircleGeometryProcessor"; }
77
78 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
79 GLSLProcessor::GenKey(*this, caps, b);
80 }
81
82 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
83 return new GLSLProcessor();
84 }
85
86private:
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
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>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500209 b->addBool(cgp.fStroke, "stroked");
210 b->addBool(cgp.fInClipPlane.isInitialized(), "clipPlane");
211 b->addBool(cgp.fInIsectPlane.isInitialized(), "isectPlane");
212 b->addBool(cgp.fInUnionPlane.isInitialized(), "unionPlane");
213 b->addBool(cgp.fInRoundCapCenters.isInitialized(), "roundCapCenters");
214 b->addBits(kMatrixKeyBits, ComputeMatrixKey(cgp.fLocalMatrix), "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215 }
216
Brian Osman609f1592020-07-01 15:14:39 -0400217 void setData(const GrGLSLProgramDataManager& pdman,
218 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400219 this->setTransform(pdman, fLocalMatrixUniform,
220 primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
221 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700222 }
223
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400225 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400226
227 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
228 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229 };
230
Brian Salomon289e3d82016-12-14 15:52:56 -0500231 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400232
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500233 Attribute fInPosition;
234 Attribute fInColor;
235 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400236 // Optional attributes.
237 Attribute fInClipPlane;
238 Attribute fInIsectPlane;
239 Attribute fInUnionPlane;
240 Attribute fInRoundCapCenters;
241
Brian Salomon289e3d82016-12-14 15:52:56 -0500242 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400243 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000244
John Stiles7571f9e2020-09-02 22:42:33 -0400245 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246};
247
bsalomoncdaa97b2016-03-08 08:30:14 -0800248GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249
Hal Canary6f6961e2017-01-31 13:50:44 -0500250#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500251GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400252 bool stroke = d->fRandom->nextBool();
253 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500254 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400255 bool clipPlane = d->fRandom->nextBool();
256 bool isectPlane = d->fRandom->nextBool();
257 bool unionPlane = d->fRandom->nextBool();
258 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500259 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
260 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261}
Hal Canary6f6961e2017-01-31 13:50:44 -0500262#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400264class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
265public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500266 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
267 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600268 return arena->make([&](void* ptr) {
269 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
270 });
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 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
287 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
288 , fLocalMatrix(localMatrix) {
289 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
290 fInColor = MakeColorAttribute("inColor", wideColor);
291 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
292 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
293 this->setVertexAttributes(&fInPosition, 4);
294 }
295
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400296 class GLSLProcessor : public GrGLSLGeometryProcessor {
297 public:
298 GLSLProcessor() {}
299
300 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
301 const ButtCapDashedCircleGeometryProcessor& bcscgp =
302 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
303 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
304 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
305 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
306 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
307
308 // emit attributes
309 varyingHandler->emitAttributes(bcscgp);
310 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500311 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400312
313 fragBuilder->codeAppend("float4 dashParams;");
314 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500315 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400316 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
317 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
318 varyingHandler->addVarying("wrapDashes", &wrapDashes,
319 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
320 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
321 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
322 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500323 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400324 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
325 // x = length of on interval, y = length of on + off.
326 // There are two other parameters in dashParams.zw:
327 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
328 // Each interval has a "corresponding" dash which may be shifted partially or
329 // fully out of its interval by the phase. So there may be up to two "visual"
330 // dashes in an interval.
331 // When computing coverage in an interval we look at three dashes. These are the
332 // "corresponding" dashes from the current, previous, and next intervals. Any of these
333 // may be phase shifted into our interval or even when phase=0 they may be within half a
334 // pixel distance of a pixel center in the interval.
335 // When in the first interval we need to check the dash from the last interval. And
336 // similarly when in the last interval we need to check the dash from the first
337 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
338 // We compute the dash begin/end angles in the vertex shader and apply them in the
339 // fragment shader when we detect we're in the first/last interval.
340 vertBuilder->codeAppend(R"(
341 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
342 // to the fragment shader as a varying.
343 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500344 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400345 // We can happen to be perfectly divisible.
346 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500347 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400348 }
349 // Let 'l' be the last interval before reaching 2 pi.
350 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
351 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
352 // interval.
353 half offset = 0;
354 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500355 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400356 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 }
359 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
360 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
361 // min.
362 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
363
364 // Based on the phase determine whether the -1st, 0th, or 1st interval's
365 // "corresponding" dash appears in the 0th interval and is closest to l.
366 offset = 0;
367 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500368 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400369 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500370 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400371 }
372 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
373 wrapDashes.w = wrapDashes.z + dashParams.x;
374 // The start of the dash we're considering may be clipped by the start of the
375 // circle.
376 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
377 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500378 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400379 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
380 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
381 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
382
383 // setup pass through color
384 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500385 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400386 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
387
388 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500389 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400390 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
391 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
392 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400393
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400394 GrShaderVar fnArgs[] = {
395 GrShaderVar("angleToEdge", kFloat_GrSLType),
396 GrShaderVar("diameter", kFloat_GrSLType),
397 };
John Stiles6b58a332020-10-26 17:53:06 -0400398 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
399 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400400 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400401 float linearDist;
402 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
403 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400404 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400405 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400406 fragBuilder->codeAppend(R"(
407 float d = length(circleEdge.xy) * circleEdge.z;
408
409 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500410 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400411 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500412 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400413 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400414 edgeAlpha *= innerAlpha;
415
Ethan Nicholase1f55022019-02-05 17:17:40 -0500416 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400417 angleFromStart = mod(angleFromStart, 6.28318530718);
418 float x = mod(angleFromStart, dashParams.y);
419 // Convert the radial distance from center to pixel into a diameter.
420 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500421 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
422 half(dashParams.w));
423 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
424 half(dashParams.y) + half(dashParams.x) -
425 half(dashParams.w));
426 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
427 half(-dashParams.y) + half(dashParams.x) -
428 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400429 half dashAlpha = 0;
430 )");
431 fragBuilder->codeAppendf(R"(
432 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500433 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400434 currDash.y = min(currDash.y, lastIntervalLength);
435 if (nextDash.x >= lastIntervalLength) {
436 // The next dash is outside the 0..2pi range, throw it away
437 nextDash.xy = half2(1000);
438 } else {
439 // Clip the end of the next dash to the end of the circle
440 nextDash.y = min(nextDash.y, lastIntervalLength);
441 }
442 }
443 )", fnName.c_str(), fnName.c_str());
444 fragBuilder->codeAppendf(R"(
445 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500446 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400447 currDash.x = max(currDash.x, 0);
448 if (prevDash.y <= 0) {
449 // The previous dash is outside the 0..2pi range, throw it away
450 prevDash.xy = half2(1000);
451 } else {
452 // Clip the start previous dash to the start of the circle
453 prevDash.x = max(prevDash.x, 0);
454 }
455 }
456 )", fnName.c_str(), fnName.c_str());
457 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500458 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
459 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
460 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400461 dashAlpha = min(dashAlpha, 1);
462 edgeAlpha *= dashAlpha;
463 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
464 fnName.c_str());
465 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
466 }
467
468 static void GenKey(const GrGeometryProcessor& gp,
469 const GrShaderCaps&,
470 GrProcessorKeyBuilder* b) {
471 const ButtCapDashedCircleGeometryProcessor& bcscgp =
472 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500473 b->addBits(kMatrixKeyBits, ComputeMatrixKey(bcscgp.fLocalMatrix), "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400474 }
475
Brian Osman609f1592020-07-01 15:14:39 -0400476 void setData(const GrGLSLProgramDataManager& pdman,
477 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400478 this->setTransform(pdman, fLocalMatrixUniform,
479 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
480 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400481 }
482
483 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400484 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400485
486 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
487 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400488 };
489
490 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500491 Attribute fInPosition;
492 Attribute fInColor;
493 Attribute fInCircleEdge;
494 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400495
496 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
497
John Stiles7571f9e2020-09-02 22:42:33 -0400498 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400499};
500
501#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500502GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500503 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400504 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500505 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400506}
507#endif
508
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000509///////////////////////////////////////////////////////////////////////////////
510
511/**
512 * 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 +0000513 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
514 * in both x and y directions.
515 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000516 * 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 +0000517 */
518
bsalomoncdaa97b2016-03-08 08:30:14 -0800519class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000520public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500521 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
522 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600523 return arena->make([&](void* ptr) {
524 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
525 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500526 }
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:
Greg Daniel2655ede2019-04-10 00:49:28 +0000541 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400542 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500543 : INHERITED(kEllipseGeometryProcessor_ClassID)
544 , fLocalMatrix(localMatrix)
545 , fStroke(stroke)
546 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500547 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500548 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400549 if (useScale) {
550 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
551 } else {
552 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
553 }
554 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500555 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000556 }
557
egdaniel57d3b032015-11-13 11:57:27 -0800558 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800560 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561
Brian Salomon289e3d82016-12-14 15:52:56 -0500562 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800563 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800564 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800565 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800566 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000567
joshualittabb52a12015-01-13 15:02:10 -0800568 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800569 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800570
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400571 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
572 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800573 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800574 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500575 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000576
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400577 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800578 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500579 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800580
Chris Dalton60283612018-02-14 13:38:14 -0700581 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700582 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500583 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800584
joshualittabb52a12015-01-13 15:02:10 -0800585 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500586 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400587 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
588 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
589 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800590
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400591 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
592 // to compute both the edges because we need two separate test equations for
593 // the single offset.
594 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
595 // the distance by the gradient, non-uniformly scaled by the inverse of the
596 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800597
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400598 // On medium precision devices, we scale the denominator of the distance equation
599 // before taking the inverse square root to minimize the chance that we're dividing
600 // by zero, then we scale the result back.
601
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000602 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400603 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400604 if (egp.fStroke) {
605 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
606 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400607 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
608 if (egp.fUseScale) {
609 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
610 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
611 } else {
612 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
613 }
614 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700615
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000616 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400617 if (args.fShaderCaps->floatIs32Bits()) {
618 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
619 } else {
620 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
621 }
622 if (egp.fUseScale) {
623 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
624 ellipseOffsets.fsIn());
625 } else {
626 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
627 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000628 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000629
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000630 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800631 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400632 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800633 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400634 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 if (egp.fUseScale) {
636 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
637 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
638 } else {
639 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
640 }
641 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
642 if (!args.fShaderCaps->floatIs32Bits()) {
643 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
644 }
645 if (egp.fUseScale) {
646 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
647 ellipseOffsets.fsIn());
648 } else {
649 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
650 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000651 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000652 }
653
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400654 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655 }
656
robertphillips46d36f02015-01-18 08:14:14 -0800657 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500658 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700659 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800660 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500661 b->addBool(egp.fStroke, "stroked");
662 b->addBits(kMatrixKeyBits, ComputeMatrixKey(egp.fLocalMatrix), "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 }
664
Brian Osman609f1592020-07-01 15:14:39 -0400665 void setData(const GrGLSLProgramDataManager& pdman,
666 const GrPrimitiveProcessor& primProc) override {
bsalomona624bf32016-09-20 09:12:47 -0700667 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400668 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700669 }
670
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000671 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400672 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400673
674 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
675 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000676 };
677
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500678 Attribute fInPosition;
679 Attribute fInColor;
680 Attribute fInEllipseOffset;
681 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400682
joshualitte3ababe2015-05-15 07:56:07 -0700683 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000684 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400685 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000686
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400687 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000688
John Stiles7571f9e2020-09-02 22:42:33 -0400689 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690};
691
bsalomoncdaa97b2016-03-08 08:30:14 -0800692GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693
Hal Canary6f6961e2017-01-31 13:50:44 -0500694#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500695GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
696 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
697 d->fRandom->nextBool(), d->fRandom->nextBool(),
698 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000699}
Hal Canary6f6961e2017-01-31 13:50:44 -0500700#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000701
702///////////////////////////////////////////////////////////////////////////////
703
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000704/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000705 * 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 +0000706 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
707 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
708 * using differentials.
709 *
710 * The result is device-independent and can be used with any affine matrix.
711 */
712
bsalomoncdaa97b2016-03-08 08:30:14 -0800713enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000714
bsalomoncdaa97b2016-03-08 08:30:14 -0800715class DIEllipseGeometryProcessor : public GrGeometryProcessor {
716public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500717 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
718 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600719 return arena->make([&](void* ptr) {
720 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
721 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500722 }
723
724 ~DIEllipseGeometryProcessor() override {}
725
726 const char* name() const override { return "DIEllipseGeometryProcessor"; }
727
728 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
729 GLSLProcessor::GenKey(*this, caps, b);
730 }
731
732 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
733 return new GLSLProcessor();
734 }
735
736private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000737 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400738 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400739 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400740 , fViewMatrix(viewMatrix)
741 , fUseScale(useScale)
742 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500743 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500744 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400745 if (useScale) {
746 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
747 kFloat3_GrSLType};
748 } else {
749 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
750 kFloat2_GrSLType};
751 }
752 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500753 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000754 }
755
egdaniel57d3b032015-11-13 11:57:27 -0800756 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500758 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000759
joshualitt465283c2015-09-11 08:19:35 -0700760 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800761 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800762 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800763 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800764 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000765
joshualittabb52a12015-01-13 15:02:10 -0800766 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800767 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800768
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400769 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
770 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800771 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500772 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700773
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400774 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800775 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500776 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800777
Chris Dalton60283612018-02-14 13:38:14 -0700778 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800780
joshualittabb52a12015-01-13 15:02:10 -0800781 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400782 this->writeOutputPosition(vertBuilder,
783 uniformHandler,
784 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500785 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400786 diegp.fViewMatrix,
787 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400788 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800789
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000790 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400791 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
792 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
793 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
794 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500795 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400796 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
797 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500798 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 if (diegp.fUseScale) {
800 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
801 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000802
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000804 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400805 if (args.fShaderCaps->floatIs32Bits()) {
806 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
807 } else {
808 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
809 }
810 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
811 if (diegp.fUseScale) {
812 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
813 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800814 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000815 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000816 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
817 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000818 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000819 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000820 }
821
822 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800823 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800824 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
825 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400826 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
827 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500828 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400829 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
830 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500831 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400832 if (diegp.fUseScale) {
833 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
834 }
835 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
836 if (!args.fShaderCaps->floatIs32Bits()) {
837 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
838 }
839 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
840 if (diegp.fUseScale) {
841 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
842 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000843 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000844 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000845
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400846 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 }
848
robertphillips46d36f02015-01-18 08:14:14 -0800849 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500850 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700851 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800852 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500853 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
854 b->addBits(kMatrixKeyBits, ComputeMatrixKey(diegp.fViewMatrix), "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000855 }
856
Brian Osman609f1592020-07-01 15:14:39 -0400857 void setData(const GrGLSLProgramDataManager& pdman,
858 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800859 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700860
Michael Ludwig553db622020-06-19 10:47:30 -0400861 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000862 }
863
864 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400865 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700866 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800867
John Stiles7571f9e2020-09-02 22:42:33 -0400868 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000869 };
870
Brian Salomon92be2f72018-06-19 14:33:47 -0400871
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500872 Attribute fInPosition;
873 Attribute fInColor;
874 Attribute fInEllipseOffsets0;
875 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400876
Brian Salomon289e3d82016-12-14 15:52:56 -0500877 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400878 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500879 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000880
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400881 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000882
John Stiles7571f9e2020-09-02 22:42:33 -0400883 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000884};
885
bsalomoncdaa97b2016-03-08 08:30:14 -0800886GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000887
Hal Canary6f6961e2017-01-31 13:50:44 -0500888#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500889GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
890 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
891 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
892 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000893}
Hal Canary6f6961e2017-01-31 13:50:44 -0500894#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000895
896///////////////////////////////////////////////////////////////////////////////
897
jvanverth6ca48822016-10-07 06:57:32 -0700898// We have two possible cases for geometry for a circle:
899
900// In the case of a normal fill, we draw geometry for the circle as an octagon.
901static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500902 // enter the octagon
903 // clang-format off
904 0, 1, 8, 1, 2, 8,
905 2, 3, 8, 3, 4, 8,
906 4, 5, 8, 5, 6, 8,
907 6, 7, 8, 7, 0, 8
908 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700909};
910
911// For stroked circles, we use two nested octagons.
912static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500913 // enter the octagon
914 // clang-format off
915 0, 1, 9, 0, 9, 8,
916 1, 2, 10, 1, 10, 9,
917 2, 3, 11, 2, 11, 10,
918 3, 4, 12, 3, 12, 11,
919 4, 5, 13, 4, 13, 12,
920 5, 6, 14, 5, 14, 13,
921 6, 7, 15, 6, 15, 14,
922 7, 0, 8, 7, 8, 15,
923 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700924};
925
Brian Osman9d958b52018-11-13 12:46:56 -0500926// Normalized geometry for octagons that circumscribe and lie on a circle:
927
928static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
929static constexpr SkPoint kOctagonOuter[] = {
930 SkPoint::Make(-kOctOffset, -1),
931 SkPoint::Make( kOctOffset, -1),
932 SkPoint::Make( 1, -kOctOffset),
933 SkPoint::Make( 1, kOctOffset),
934 SkPoint::Make( kOctOffset, 1),
935 SkPoint::Make(-kOctOffset, 1),
936 SkPoint::Make(-1, kOctOffset),
937 SkPoint::Make(-1, -kOctOffset),
938};
939
940// cosine and sine of pi/8
941static constexpr SkScalar kCosPi8 = 0.923579533f;
942static constexpr SkScalar kSinPi8 = 0.382683432f;
943static constexpr SkPoint kOctagonInner[] = {
944 SkPoint::Make(-kSinPi8, -kCosPi8),
945 SkPoint::Make( kSinPi8, -kCosPi8),
946 SkPoint::Make( kCosPi8, -kSinPi8),
947 SkPoint::Make( kCosPi8, kSinPi8),
948 SkPoint::Make( kSinPi8, kCosPi8),
949 SkPoint::Make(-kSinPi8, kCosPi8),
950 SkPoint::Make(-kCosPi8, kSinPi8),
951 SkPoint::Make(-kCosPi8, -kSinPi8),
952};
Brian Salomon289e3d82016-12-14 15:52:56 -0500953
jvanverth6ca48822016-10-07 06:57:32 -0700954static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
955static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
956static const int kVertsPerStrokeCircle = 16;
957static const int kVertsPerFillCircle = 9;
958
959static int circle_type_to_vert_count(bool stroked) {
960 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
961}
962
963static int circle_type_to_index_count(bool stroked) {
964 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
965}
966
967static const uint16_t* circle_type_to_indices(bool stroked) {
968 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
969}
970
971///////////////////////////////////////////////////////////////////////////////
972
Brian Salomon05441c42017-05-15 16:45:49 -0400973class CircleOp final : public GrMeshDrawOp {
974private:
975 using Helper = GrSimpleMeshDrawOpHelper;
976
joshualitt76e7fb62015-02-11 08:52:27 -0800977public:
Brian Salomon25a88092016-12-01 09:36:50 -0500978 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700979
bsalomon4f3a0ca2016-08-22 13:14:26 -0700980 /** Optional extra params to render a partial arc rather than a full circle. */
981 struct ArcParams {
982 SkScalar fStartAngleRadians;
983 SkScalar fSweepAngleRadians;
984 bool fUseCenter;
985 };
Brian Salomon05441c42017-05-15 16:45:49 -0400986
Herb Derbyc76d4092020-10-07 16:46:15 -0400987 static GrOp::Owner Make(GrRecordingContext* context,
988 GrPaint&& paint,
989 const SkMatrix& viewMatrix,
990 SkPoint center,
991 SkScalar radius,
992 const GrStyle& style,
993 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700994 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700995 if (style.hasPathEffect()) {
996 return nullptr;
997 }
Brian Salomon05441c42017-05-15 16:45:49 -0400998 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 SkStrokeRec::Style recStyle = stroke.getStyle();
1000 if (arcParams) {
1001 // Arc support depends on the style.
1002 switch (recStyle) {
1003 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001004 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001005 return nullptr;
1006 case SkStrokeRec::kFill_Style:
1007 // This supports all fills.
1008 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001009 case SkStrokeRec::kStroke_Style:
1010 // Strokes that don't use the center point are supported with butt and round
1011 // caps.
1012 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1013 return nullptr;
1014 }
1015 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001016 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001017 // Hairline only supports butt cap. Round caps could be emulated by slightly
1018 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001019 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1020 return nullptr;
1021 }
1022 break;
1023 }
1024 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001025 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1026 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001027 }
1028
Herb Derbyc76d4092020-10-07 16:46:15 -04001029 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001030 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1031 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001032 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001033 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001034 const SkStrokeRec& stroke = style.strokeRec();
1035 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001036
Brian Salomon45c92202018-04-10 10:53:58 -04001037 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001038
bsalomon4b4a7cc2016-07-08 04:42:54 -07001039 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001040 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001041 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001042
Brian Salomon289e3d82016-12-14 15:52:56 -05001043 bool isStrokeOnly =
1044 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001045 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001046
jvanverth6ca48822016-10-07 06:57:32 -07001047 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001048 SkScalar outerRadius = radius;
1049 SkScalar halfWidth = 0;
1050 if (hasStroke) {
1051 if (SkScalarNearlyZero(strokeWidth)) {
1052 halfWidth = SK_ScalarHalf;
1053 } else {
1054 halfWidth = SkScalarHalf(strokeWidth);
1055 }
1056
1057 outerRadius += halfWidth;
1058 if (isStrokeOnly) {
1059 innerRadius = radius - halfWidth;
1060 }
1061 }
1062
1063 // The radii are outset for two reasons. First, it allows the shader to simply perform
1064 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1065 // Second, the outer radius is used to compute the verts of the bounding box that is
1066 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001067 outerRadius += SK_ScalarHalf;
1068 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001069 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001070 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001071
bsalomon4f3a0ca2016-08-22 13:14:26 -07001072 // This makes every point fully inside the intersection plane.
1073 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1074 // This makes every point fully outside the union plane.
1075 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001076 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001077 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1078 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001079 if (arcParams) {
1080 // The shader operates in a space where the circle is translated to be centered at the
1081 // origin. Here we compute points on the unit circle at the starting and ending angles.
1082 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001083 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1084 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001085 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001086 stopPoint.fY = SkScalarSin(endAngle);
1087 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001088
1089 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1090 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1091 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1092 startPoint.normalize();
1093 stopPoint.normalize();
1094
Brian Salomon3517aa72019-12-11 08:16:22 -05001095 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1096 // should orient the clip planes for arcs.
1097 SkASSERT(viewMatrix.isSimilarity());
1098 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1099 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1100 if (upperLeftDet < 0) {
1101 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001102 }
1103
Brian Salomon45c92202018-04-10 10:53:58 -04001104 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1105 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1106 SkPoint roundCaps[2];
1107 if (fRoundCaps) {
1108 // Compute the cap center points in the normalized space.
1109 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1110 roundCaps[0] = startPoint * midRadius;
1111 roundCaps[1] = stopPoint * midRadius;
1112 } else {
1113 roundCaps[0] = kUnusedRoundCaps[0];
1114 roundCaps[1] = kUnusedRoundCaps[1];
1115 }
1116
bsalomon4f3a0ca2016-08-22 13:14:26 -07001117 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001118 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1119 // center of the butts.
1120 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001121 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001122 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001123 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001124 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1125 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1126 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 if (useCenter) {
1128 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1129 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001130 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1131 if (arcParams->fSweepAngleRadians < 0) {
1132 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001133 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001134 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001135 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001136 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001137 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001138 color,
1139 innerRadius,
1140 outerRadius,
1141 {norm0.fX, norm0.fY, 0.5f},
1142 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1143 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001144 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001145 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001146 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001147 fClipPlaneIsect = false;
1148 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001149 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001150 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001151 color,
1152 innerRadius,
1153 outerRadius,
1154 {norm0.fX, norm0.fY, 0.5f},
1155 {norm1.fX, norm1.fY, 0.5f},
1156 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001157 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001158 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001159 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001160 fClipPlaneIsect = true;
1161 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001162 }
1163 } else {
1164 // We clip to a secant of the original circle.
1165 startPoint.scale(radius);
1166 stopPoint.scale(radius);
1167 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1168 norm.normalize();
1169 if (arcParams->fSweepAngleRadians > 0) {
1170 norm.negate();
1171 }
1172 SkScalar d = -norm.dot(startPoint) + 0.5f;
1173
Brian Salomon05441c42017-05-15 16:45:49 -04001174 fCircles.emplace_back(
1175 Circle{color,
1176 innerRadius,
1177 outerRadius,
1178 {norm.fX, norm.fY, d},
1179 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1180 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001181 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001182 devBounds,
1183 stroked});
1184 fClipPlane = true;
1185 fClipPlaneIsect = false;
1186 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001187 }
1188 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001189 fCircles.emplace_back(
1190 Circle{color,
1191 innerRadius,
1192 outerRadius,
1193 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1194 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1195 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001196 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001197 devBounds,
1198 stroked});
1199 fClipPlane = false;
1200 fClipPlaneIsect = false;
1201 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001202 }
bsalomon88cf17d2016-07-08 06:40:56 -07001203 // Use the original radius and stroke radius for the bounds so that it does not include the
1204 // AA bloat.
1205 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001206 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001207 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001208 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001209 fVertCount = circle_type_to_vert_count(stroked);
1210 fIndexCount = circle_type_to_index_count(stroked);
1211 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001212 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001213
Brian Salomon289e3d82016-12-14 15:52:56 -05001214 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001215
Chris Dalton1706cbf2019-05-21 19:35:29 -06001216 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001217 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001218 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001219 } else {
1220 fHelper.visitProxies(func);
1221 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001222 }
1223
Chris Dalton6ce447a2019-06-23 18:07:38 -06001224 GrProcessorSet::Analysis finalize(
1225 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1226 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001227 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001228 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001229 GrProcessorAnalysisCoverage::kSingleChannel, color,
1230 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001231 }
1232
1233 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1234
bsalomone46f9fe2015-08-18 06:05:14 -07001235private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001236 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001237
Robert Phillips4133dc42020-03-11 15:55:55 -04001238 void onCreateProgramInfo(const GrCaps* caps,
1239 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001240 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001241 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001242 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001243 GrXferBarrierFlags renderPassXferBarriers,
1244 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001245 SkMatrix localMatrix;
1246 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001247 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001248 }
1249
Robert Phillips4490d922020-03-03 14:50:59 -05001250 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001251 fClipPlaneIsect, fClipPlaneUnion,
1252 fRoundCaps, fWideColor,
1253 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001254
Brian Salomon8afde5f2020-04-01 16:22:00 -04001255 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001256 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001257 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001258 }
1259
Robert Phillips4490d922020-03-03 14:50:59 -05001260 void onPrepareDraws(Target* target) override {
1261 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001262 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001263 if (!fProgramInfo) {
1264 return;
1265 }
1266 }
1267
Brian Salomon12d22642019-01-29 14:38:50 -05001268 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001269 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001270 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1271 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001272 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001273 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001274 return;
1275 }
1276
Brian Salomon12d22642019-01-29 14:38:50 -05001277 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001278 int firstIndex = 0;
1279 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1280 if (!indices) {
1281 SkDebugf("Could not allocate indices\n");
1282 return;
1283 }
1284
1285 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001286 for (const auto& circle : fCircles) {
1287 SkScalar innerRadius = circle.fInnerRadius;
1288 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001289 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001290 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001291
joshualitt76e7fb62015-02-11 08:52:27 -08001292 // The inner radius in the vertex data must be specified in normalized space.
1293 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001294 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001295
1296 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001297 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001298
Brian Osman9a24fee2018-08-03 09:48:42 -04001299 SkVector geoClipPlane = { 0, 0 };
1300 SkScalar offsetClipDist = SK_Scalar1;
1301 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1302 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1303 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1304 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1305 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1306 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1307 // the AA can extend just past the center of the circle.
1308 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1309 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1310 SkAssertResult(geoClipPlane.normalize());
1311 offsetClipDist = 0.5f / halfWidth;
1312 }
1313
Brian Osman7d8f82b2018-11-08 10:24:09 -05001314 for (int i = 0; i < 8; ++i) {
1315 // This clips the normalized offset to the half-plane we computed above. Then we
1316 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001317 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001318 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001319 vertices.write(center + offset * halfWidth,
1320 color,
1321 offset,
1322 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001323 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001324 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001325 }
1326 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001327 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001328 }
1329 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001330 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001331 }
1332 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001334 }
Brian Salomon45c92202018-04-10 10:53:58 -04001335 }
jvanverth6ca48822016-10-07 06:57:32 -07001336
Brian Salomon05441c42017-05-15 16:45:49 -04001337 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001338 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001339
Brian Osman7d8f82b2018-11-08 10:24:09 -05001340 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001341 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1342 color,
1343 kOctagonInner[i] * innerRadius,
1344 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001345 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001346 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 }
1348 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
1354 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
Brian Salomon45c92202018-04-10 10:53:58 -04001357 }
jvanverth6ca48822016-10-07 06:57:32 -07001358 } else {
1359 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001360 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001361 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001362 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001363 }
jvanverth6ca48822016-10-07 06:57:32 -07001364 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001366 }
1367 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001369 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001370 if (fRoundCaps) {
1371 vertices.write(circle.fRoundCapCenters);
1372 }
jvanverth6ca48822016-10-07 06:57:32 -07001373 }
1374
Brian Salomon05441c42017-05-15 16:45:49 -04001375 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1376 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001377 for (int i = 0; i < primIndexCount; ++i) {
1378 *indices++ = primIndices[i] + currStartVertex;
1379 }
1380
Brian Salomon05441c42017-05-15 16:45:49 -04001381 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001382 }
jvanverth6ca48822016-10-07 06:57:32 -07001383
Robert Phillips4490d922020-03-03 14:50:59 -05001384 fMesh = target->allocMesh();
1385 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001386 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001387 }
1388
1389 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001390 if (!fProgramInfo || !fMesh) {
1391 return;
1392 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001393
Chris Dalton765ed362020-03-16 17:34:44 -06001394 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1395 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1396 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001397 }
1398
Herb Derbye25c3002020-10-27 15:57:27 -04001399 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001400 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001401
1402 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001403 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001404 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001405 }
1406
Brian Salomon05441c42017-05-15 16:45:49 -04001407 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001408 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001409 }
1410
Brian Salomon05441c42017-05-15 16:45:49 -04001411 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001412 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1413 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001414 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001415 }
1416
Brian Salomon289e3d82016-12-14 15:52:56 -05001417 // Because we've set up the ops that don't use the planes with noop values
1418 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001419 fClipPlane |= that->fClipPlane;
1420 fClipPlaneIsect |= that->fClipPlaneIsect;
1421 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001422 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001423 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001424
Brian Salomon05441c42017-05-15 16:45:49 -04001425 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001426 fVertCount += that->fVertCount;
1427 fIndexCount += that->fIndexCount;
1428 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001429 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001430 }
1431
John Stilesaf366522020-08-13 09:57:34 -04001432#if GR_TEST_UTILS
1433 SkString onDumpInfo() const override {
1434 SkString string;
1435 for (int i = 0; i < fCircles.count(); ++i) {
1436 string.appendf(
1437 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1438 "InnerRad: %.2f, OuterRad: %.2f\n",
1439 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1440 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1441 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1442 fCircles[i].fOuterRadius);
1443 }
1444 string += fHelper.dumpInfo();
1445 return string;
1446 }
1447#endif
1448
Brian Salomon05441c42017-05-15 16:45:49 -04001449 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001450 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001451 SkScalar fInnerRadius;
1452 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001453 SkScalar fClipPlane[3];
1454 SkScalar fIsectPlane[3];
1455 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001456 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001457 SkRect fDevBounds;
1458 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001459 };
1460
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001462 Helper fHelper;
1463 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001464 int fVertCount;
1465 int fIndexCount;
1466 bool fAllFill;
1467 bool fClipPlane;
1468 bool fClipPlaneIsect;
1469 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001470 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001471 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001472
Chris Daltoneb694b72020-03-16 09:25:50 -06001473 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001474 GrProgramInfo* fProgramInfo = nullptr;
1475
John Stiles7571f9e2020-09-02 22:42:33 -04001476 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001477};
1478
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001479class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1480private:
1481 using Helper = GrSimpleMeshDrawOpHelper;
1482
1483public:
1484 DEFINE_OP_CLASS_ID
1485
Herb Derbyc76d4092020-10-07 16:46:15 -04001486 static GrOp::Owner Make(GrRecordingContext* context,
1487 GrPaint&& paint,
1488 const SkMatrix& viewMatrix,
1489 SkPoint center,
1490 SkScalar radius,
1491 SkScalar strokeWidth,
1492 SkScalar startAngle,
1493 SkScalar onAngle,
1494 SkScalar offAngle,
1495 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001496 SkASSERT(circle_stays_circle(viewMatrix));
1497 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001498 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1499 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001500 onAngle, offAngle, phaseAngle);
1501 }
1502
Herb Derbyc76d4092020-10-07 16:46:15 -04001503 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001504 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1505 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1506 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001507 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001508 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001509 SkASSERT(circle_stays_circle(viewMatrix));
1510 viewMatrix.mapPoints(&center, 1);
1511 radius = viewMatrix.mapRadius(radius);
1512 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1513
1514 // Determine the angle where the circle starts in device space and whether its orientation
1515 // has been reversed.
1516 SkVector start;
1517 bool reflection;
1518 if (!startAngle) {
1519 start = {1, 0};
1520 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001521 start.fY = SkScalarSin(startAngle);
1522 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001523 }
1524 viewMatrix.mapVectors(&start, 1);
1525 startAngle = SkScalarATan2(start.fY, start.fX);
1526 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1527 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1528
1529 auto totalAngle = onAngle + offAngle;
1530 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1531
1532 SkScalar halfWidth = 0;
1533 if (SkScalarNearlyZero(strokeWidth)) {
1534 halfWidth = SK_ScalarHalf;
1535 } else {
1536 halfWidth = SkScalarHalf(strokeWidth);
1537 }
1538
1539 SkScalar outerRadius = radius + halfWidth;
1540 SkScalar innerRadius = radius - halfWidth;
1541
1542 // The radii are outset for two reasons. First, it allows the shader to simply perform
1543 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1544 // Second, the outer radius is used to compute the verts of the bounding box that is
1545 // rendered and the outset ensures the box will cover all partially covered by the circle.
1546 outerRadius += SK_ScalarHalf;
1547 innerRadius -= SK_ScalarHalf;
1548 fViewMatrixIfUsingLocalCoords = viewMatrix;
1549
1550 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1551 center.fX + outerRadius, center.fY + outerRadius);
1552
1553 // We store whether there is a reflection as a negative total angle.
1554 if (reflection) {
1555 totalAngle = -totalAngle;
1556 }
1557 fCircles.push_back(Circle{
1558 color,
1559 outerRadius,
1560 innerRadius,
1561 onAngle,
1562 totalAngle,
1563 startAngle,
1564 phaseAngle,
1565 devBounds
1566 });
1567 // Use the original radius and stroke radius for the bounds so that it does not include the
1568 // AA bloat.
1569 radius += halfWidth;
1570 this->setBounds(
1571 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001572 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001573 fVertCount = circle_type_to_vert_count(true);
1574 fIndexCount = circle_type_to_index_count(true);
1575 }
1576
1577 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1578
Chris Dalton1706cbf2019-05-21 19:35:29 -06001579 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001580 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001581 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001582 } else {
1583 fHelper.visitProxies(func);
1584 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001585 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001586
Chris Dalton6ce447a2019-06-23 18:07:38 -06001587 GrProcessorSet::Analysis finalize(
1588 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1589 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001590 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001591 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001592 GrProcessorAnalysisCoverage::kSingleChannel, color,
1593 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001594 }
1595
1596 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1597
1598private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001599 GrProgramInfo* programInfo() override { return fProgramInfo; }
1600
Robert Phillips4133dc42020-03-11 15:55:55 -04001601 void onCreateProgramInfo(const GrCaps* caps,
1602 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001603 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001604 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001605 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001606 GrXferBarrierFlags renderPassXferBarriers,
1607 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001608 SkMatrix localMatrix;
1609 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001610 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001611 }
1612
1613 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001614 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001615 fWideColor,
1616 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001617
Brian Salomon8afde5f2020-04-01 16:22:00 -04001618 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001619 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001620 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001621 }
1622
Robert Phillips4490d922020-03-03 14:50:59 -05001623 void onPrepareDraws(Target* target) override {
1624 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001625 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001626 if (!fProgramInfo) {
1627 return;
1628 }
1629 }
1630
Brian Salomon12d22642019-01-29 14:38:50 -05001631 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001632 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001633 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1634 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001635 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001636 SkDebugf("Could not allocate vertices\n");
1637 return;
1638 }
1639
Brian Salomon12d22642019-01-29 14:38:50 -05001640 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001641 int firstIndex = 0;
1642 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1643 if (!indices) {
1644 SkDebugf("Could not allocate indices\n");
1645 return;
1646 }
1647
1648 int currStartVertex = 0;
1649 for (const auto& circle : fCircles) {
1650 // The inner radius in the vertex data must be specified in normalized space so that
1651 // length() can be called with smaller values to avoid precision issues with half
1652 // floats.
1653 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1654 const SkRect& bounds = circle.fDevBounds;
1655 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001656 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1657 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1658 };
1659 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001661 dashParams.totalAngle = -dashParams.totalAngle;
1662 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001663 }
1664
Brian Osmane3caf2d2018-11-21 13:48:36 -05001665 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001666
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 // The bounding geometry for the circle is composed of an outer bounding octagon and
1668 // an inner bounded octagon.
1669
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001670 // Compute the vertices of the outer octagon.
1671 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1672 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001673
1674 auto reflectY = [=](const SkPoint& p) {
1675 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676 };
Brian Osman9d958b52018-11-13 12:46:56 -05001677
1678 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001679 vertices.write(center + kOctagonOuter[i] * halfWidth,
1680 color,
1681 reflectY(kOctagonOuter[i]),
1682 circle.fOuterRadius,
1683 normInnerRadius,
1684 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001685 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001686
1687 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001688 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001689 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1690 color,
1691 reflectY(kOctagonInner[i]) * normInnerRadius,
1692 circle.fOuterRadius,
1693 normInnerRadius,
1694 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001695 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001696
1697 const uint16_t* primIndices = circle_type_to_indices(true);
1698 const int primIndexCount = circle_type_to_index_count(true);
1699 for (int i = 0; i < primIndexCount; ++i) {
1700 *indices++ = primIndices[i] + currStartVertex;
1701 }
1702
1703 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001704 }
1705
Robert Phillips4490d922020-03-03 14:50:59 -05001706 fMesh = target->allocMesh();
1707 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001708 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001709 }
1710
1711 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001712 if (!fProgramInfo || !fMesh) {
1713 return;
1714 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001715
Chris Dalton765ed362020-03-16 17:34:44 -06001716 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1717 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1718 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001719 }
1720
Herb Derbye25c3002020-10-27 15:57:27 -04001721 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001722 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1723
1724 // can only represent 65535 unique vertices with 16-bit indices
1725 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001726 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001727 }
1728
1729 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001730 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001731 }
1732
1733 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001734 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1735 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001736 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001737 }
1738
1739 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001740 fVertCount += that->fVertCount;
1741 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001742 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001743 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001744 }
1745
John Stilesaf366522020-08-13 09:57:34 -04001746#if GR_TEST_UTILS
1747 SkString onDumpInfo() const override {
1748 SkString string;
1749 for (int i = 0; i < fCircles.count(); ++i) {
1750 string.appendf(
1751 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1752 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1753 "Phase: %.2f\n",
1754 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1755 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1756 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1757 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1758 fCircles[i].fPhaseAngle);
1759 }
1760 string += fHelper.dumpInfo();
1761 return string;
1762 }
1763#endif
1764
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001765 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001766 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001767 SkScalar fOuterRadius;
1768 SkScalar fInnerRadius;
1769 SkScalar fOnAngle;
1770 SkScalar fTotalAngle;
1771 SkScalar fStartAngle;
1772 SkScalar fPhaseAngle;
1773 SkRect fDevBounds;
1774 };
1775
1776 SkMatrix fViewMatrixIfUsingLocalCoords;
1777 Helper fHelper;
1778 SkSTArray<1, Circle, true> fCircles;
1779 int fVertCount;
1780 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001781 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001782
Chris Daltoneb694b72020-03-16 09:25:50 -06001783 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001784 GrProgramInfo* fProgramInfo = nullptr;
1785
John Stiles7571f9e2020-09-02 22:42:33 -04001786 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001787};
1788
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001789///////////////////////////////////////////////////////////////////////////////
1790
Brian Salomon05441c42017-05-15 16:45:49 -04001791class EllipseOp : public GrMeshDrawOp {
1792private:
1793 using Helper = GrSimpleMeshDrawOpHelper;
1794
1795 struct DeviceSpaceParams {
1796 SkPoint fCenter;
1797 SkScalar fXRadius;
1798 SkScalar fYRadius;
1799 SkScalar fInnerXRadius;
1800 SkScalar fInnerYRadius;
1801 };
1802
joshualitt76e7fb62015-02-11 08:52:27 -08001803public:
Brian Salomon25a88092016-12-01 09:36:50 -05001804 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001805
Herb Derbyc76d4092020-10-07 16:46:15 -04001806 static GrOp::Owner Make(GrRecordingContext* context,
1807 GrPaint&& paint,
1808 const SkMatrix& viewMatrix,
1809 const SkRect& ellipse,
1810 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001811 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001812 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001813 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1814 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001815 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1816 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001817 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1818 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1819 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1820 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001821
bsalomon4b4a7cc2016-07-08 04:42:54 -07001822 // do (potentially) anisotropic mapping of stroke
1823 SkVector scaledStroke;
1824 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001825 scaledStroke.fX = SkScalarAbs(
1826 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1827 scaledStroke.fY = SkScalarAbs(
1828 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001829
1830 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001831 bool isStrokeOnly =
1832 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001833 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1834
Brian Salomon05441c42017-05-15 16:45:49 -04001835 params.fInnerXRadius = 0;
1836 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001837 if (hasStroke) {
1838 if (SkScalarNearlyZero(scaledStroke.length())) {
1839 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1840 } else {
1841 scaledStroke.scale(SK_ScalarHalf);
1842 }
1843
1844 // we only handle thick strokes for near-circular ellipses
1845 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001846 (0.5f * params.fXRadius > params.fYRadius ||
1847 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001848 return nullptr;
1849 }
1850
1851 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001852 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1853 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1854 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1855 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001856 return nullptr;
1857 }
1858
1859 // this is legit only if scale & translation (which should be the case at the moment)
1860 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001861 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1862 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001863 }
1864
Brian Salomon05441c42017-05-15 16:45:49 -04001865 params.fXRadius += scaledStroke.fX;
1866 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001867 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001868
1869 // For large ovals with low precision floats, we fall back to the path renderer.
1870 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1871 // minimum value to avoid divides by zero. With large ovals and low precision this
1872 // leads to blurring at the edge of the oval.
1873 const SkScalar kMaxOvalRadius = 16384;
1874 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1875 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1876 return nullptr;
1877 }
1878
Greg Daniel2655ede2019-04-10 00:49:28 +00001879 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001880 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001881 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001882
Herb Derbyc76d4092020-10-07 16:46:15 -04001883 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001884 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001885 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001886 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001887 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001888 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001889 SkStrokeRec::Style style = stroke.getStyle();
1890 bool isStrokeOnly =
1891 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001892
Brian Salomon05441c42017-05-15 16:45:49 -04001893 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1894 params.fInnerXRadius, params.fInnerYRadius,
1895 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1896 params.fCenter.fY - params.fYRadius,
1897 params.fCenter.fX + params.fXRadius,
1898 params.fCenter.fY + params.fYRadius)});
1899
Greg Daniel5faf4742019-10-01 15:14:44 -04001900 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001901
bsalomon4b4a7cc2016-07-08 04:42:54 -07001902 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001903 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001904
Brian Salomon05441c42017-05-15 16:45:49 -04001905 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1906 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001907 }
joshualitt76e7fb62015-02-11 08:52:27 -08001908
Brian Salomon289e3d82016-12-14 15:52:56 -05001909 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001910
Chris Dalton1706cbf2019-05-21 19:35:29 -06001911 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001912 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001913 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001914 } else {
1915 fHelper.visitProxies(func);
1916 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001917 }
1918
Chris Dalton6ce447a2019-06-23 18:07:38 -06001919 GrProcessorSet::Analysis finalize(
1920 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1921 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001922 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1923 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001924 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001925 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001926 GrProcessorAnalysisCoverage::kSingleChannel, color,
1927 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001928 }
1929
1930 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1931
bsalomone46f9fe2015-08-18 06:05:14 -07001932private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001933 GrProgramInfo* programInfo() override { return fProgramInfo; }
1934
Robert Phillips4133dc42020-03-11 15:55:55 -04001935 void onCreateProgramInfo(const GrCaps* caps,
1936 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001937 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001938 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001939 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001940 GrXferBarrierFlags renderPassXferBarriers,
1941 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001942 SkMatrix localMatrix;
1943 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001944 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001945 }
1946
Robert Phillips4490d922020-03-03 14:50:59 -05001947 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1948 fUseScale, localMatrix);
1949
Brian Salomon8afde5f2020-04-01 16:22:00 -04001950 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001951 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001952 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001953 }
1954
Robert Phillips4490d922020-03-03 14:50:59 -05001955 void onPrepareDraws(Target* target) override {
1956 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001957 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001958 if (!fProgramInfo) {
1959 return;
1960 }
1961 }
1962
1963 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001964 GrVertexWriter verts{helper.vertices()};
1965 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001966 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001967 return;
1968 }
1969
Brian Salomon05441c42017-05-15 16:45:49 -04001970 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001971 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001972 SkScalar xRadius = ellipse.fXRadius;
1973 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001974
1975 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001976 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1977 SkScalarInvert(xRadius),
1978 SkScalarInvert(yRadius),
1979 SkScalarInvert(ellipse.fInnerXRadius),
1980 SkScalarInvert(ellipse.fInnerYRadius)
1981 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001982 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1983 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001984
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001985 if (!fStroked) {
1986 // For filled ellipses we map a unit circle in the vertex attributes rather than
1987 // computing an ellipse and modifying that distance, so we normalize to 1
1988 xMaxOffset /= xRadius;
1989 yMaxOffset /= yRadius;
1990 }
1991
joshualitt76e7fb62015-02-11 08:52:27 -08001992 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001993 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1994 color,
1995 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05001996 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05001997 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001998 }
Robert Phillips4490d922020-03-03 14:50:59 -05001999 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002000 }
2001
2002 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002003 if (!fProgramInfo || !fMesh) {
2004 return;
2005 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002006
Chris Dalton765ed362020-03-16 17:34:44 -06002007 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2008 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2009 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002010 }
2011
Herb Derbye25c3002020-10-27 15:57:27 -04002012 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002013 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002014
Brian Salomon05441c42017-05-15 16:45:49 -04002015 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002016 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002017 }
2018
bsalomoncdaa97b2016-03-08 08:30:14 -08002019 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002020 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002021 }
2022
Brian Salomon05441c42017-05-15 16:45:49 -04002023 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002024 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2025 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002026 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002027 }
2028
Brian Salomon05441c42017-05-15 16:45:49 -04002029 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002030 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002031 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002032 }
2033
John Stilesaf366522020-08-13 09:57:34 -04002034#if GR_TEST_UTILS
2035 SkString onDumpInfo() const override {
2036 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2037 for (const auto& geo : fEllipses) {
2038 string.appendf(
2039 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2040 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2041 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2042 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2043 geo.fInnerXRadius, geo.fInnerYRadius);
2044 }
2045 string += fHelper.dumpInfo();
2046 return string;
2047 }
2048#endif
2049
Brian Salomon05441c42017-05-15 16:45:49 -04002050 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002051 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002052 SkScalar fXRadius;
2053 SkScalar fYRadius;
2054 SkScalar fInnerXRadius;
2055 SkScalar fInnerYRadius;
2056 SkRect fDevBounds;
2057 };
joshualitt76e7fb62015-02-11 08:52:27 -08002058
Brian Salomon289e3d82016-12-14 15:52:56 -05002059 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002060 Helper fHelper;
2061 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002062 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002063 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002064 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002065
Chris Daltoneb694b72020-03-16 09:25:50 -06002066 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002067 GrProgramInfo* fProgramInfo = nullptr;
2068
John Stiles7571f9e2020-09-02 22:42:33 -04002069 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002070};
2071
joshualitt76e7fb62015-02-11 08:52:27 -08002072/////////////////////////////////////////////////////////////////////////////////////////////////
2073
Brian Salomon05441c42017-05-15 16:45:49 -04002074class DIEllipseOp : public GrMeshDrawOp {
2075private:
2076 using Helper = GrSimpleMeshDrawOpHelper;
2077
2078 struct DeviceSpaceParams {
2079 SkPoint fCenter;
2080 SkScalar fXRadius;
2081 SkScalar fYRadius;
2082 SkScalar fInnerXRadius;
2083 SkScalar fInnerYRadius;
2084 DIEllipseStyle fStyle;
2085 };
2086
joshualitt76e7fb62015-02-11 08:52:27 -08002087public:
Brian Salomon25a88092016-12-01 09:36:50 -05002088 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002089
Herb Derbyc76d4092020-10-07 16:46:15 -04002090 static GrOp::Owner Make(GrRecordingContext* context,
2091 GrPaint&& paint,
2092 const SkMatrix& viewMatrix,
2093 const SkRect& ellipse,
2094 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002095 DeviceSpaceParams params;
2096 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2097 params.fXRadius = SkScalarHalf(ellipse.width());
2098 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002099
bsalomon4b4a7cc2016-07-08 04:42:54 -07002100 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002101 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2102 ? DIEllipseStyle::kStroke
2103 : (SkStrokeRec::kHairline_Style == style)
2104 ? DIEllipseStyle::kHairline
2105 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002106
Brian Salomon05441c42017-05-15 16:45:49 -04002107 params.fInnerXRadius = 0;
2108 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002109 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2110 SkScalar strokeWidth = stroke.getWidth();
2111
2112 if (SkScalarNearlyZero(strokeWidth)) {
2113 strokeWidth = SK_ScalarHalf;
2114 } else {
2115 strokeWidth *= SK_ScalarHalf;
2116 }
2117
2118 // we only handle thick strokes for near-circular ellipses
2119 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002120 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2121 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002122 return nullptr;
2123 }
2124
2125 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002126 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2127 (strokeWidth * strokeWidth) * params.fXRadius) {
2128 return nullptr;
2129 }
2130 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2131 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002132 return nullptr;
2133 }
2134
2135 // set inner radius (if needed)
2136 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002137 params.fInnerXRadius = params.fXRadius - strokeWidth;
2138 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002139 }
2140
Brian Salomon05441c42017-05-15 16:45:49 -04002141 params.fXRadius += strokeWidth;
2142 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002143 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002144
2145 // For large ovals with low precision floats, we fall back to the path renderer.
2146 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2147 // minimum value to avoid divides by zero. With large ovals and low precision this
2148 // leads to blurring at the edge of the oval.
2149 const SkScalar kMaxOvalRadius = 16384;
2150 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2151 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2152 return nullptr;
2153 }
2154
Brian Salomon05441c42017-05-15 16:45:49 -04002155 if (DIEllipseStyle::kStroke == params.fStyle &&
2156 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2157 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002158 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002159 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002160 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002161
Herb Derbyc76d4092020-10-07 16:46:15 -04002162 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002163 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002164 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002165 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002166 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002167 // This expands the outer rect so that after CTM we end up with a half-pixel border
2168 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2169 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2170 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2171 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2172 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2173 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002174
Brian Salomon05441c42017-05-15 16:45:49 -04002175 fEllipses.emplace_back(
2176 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2177 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2178 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2179 params.fCenter.fY - params.fYRadius - geoDy,
2180 params.fCenter.fX + params.fXRadius + geoDx,
2181 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002182 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002183 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002184 }
2185
Brian Salomon289e3d82016-12-14 15:52:56 -05002186 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002187
Chris Dalton1706cbf2019-05-21 19:35:29 -06002188 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002189 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002190 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002191 } else {
2192 fHelper.visitProxies(func);
2193 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002194 }
2195
Chris Dalton6ce447a2019-06-23 18:07:38 -06002196 GrProcessorSet::Analysis finalize(
2197 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2198 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002199 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2200 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002201 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002202 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002203 GrProcessorAnalysisCoverage::kSingleChannel, color,
2204 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002205 }
2206
2207 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2208
bsalomone46f9fe2015-08-18 06:05:14 -07002209private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002210 GrProgramInfo* programInfo() override { return fProgramInfo; }
2211
Robert Phillips4133dc42020-03-11 15:55:55 -04002212 void onCreateProgramInfo(const GrCaps* caps,
2213 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002214 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002215 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002216 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002217 GrXferBarrierFlags renderPassXferBarriers,
2218 GrLoadOp colorLoadOp) 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,
Greg Daniel42dbca52020-11-20 10:22:43 -05002225 renderPassXferBarriers, colorLoadOp);
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,
Greg Daniel42dbca52020-11-20 10:22:43 -05002616 GrXferBarrierFlags renderPassXferBarriers,
2617 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002618 // Invert the view matrix as a local matrix (if any other processors require coords).
2619 SkMatrix localMatrix;
2620 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002621 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002622 }
2623
Robert Phillips4490d922020-03-03 14:50:59 -05002624 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002625 false, false, false, false,
2626 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002627
Brian Salomon8afde5f2020-04-01 16:22:00 -04002628 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002629 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002630 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002631 }
2632
Robert Phillips4490d922020-03-03 14:50:59 -05002633 void onPrepareDraws(Target* target) override {
2634 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002635 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002636 if (!fProgramInfo) {
2637 return;
2638 }
2639 }
2640
Brian Salomon12d22642019-01-29 14:38:50 -05002641 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002642 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002643
Robert Phillips4490d922020-03-03 14:50:59 -05002644 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2645 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002646 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002647 SkDebugf("Could not allocate vertices\n");
2648 return;
2649 }
2650
Brian Salomon12d22642019-01-29 14:38:50 -05002651 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002652 int firstIndex = 0;
2653 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2654 if (!indices) {
2655 SkDebugf("Could not allocate indices\n");
2656 return;
2657 }
2658
2659 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002660 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002661 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002662 SkScalar outerRadius = rrect.fOuterRadius;
2663 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002664
Brian Salomon289e3d82016-12-14 15:52:56 -05002665 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2666 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002667
Brian Salomon289e3d82016-12-14 15:52:56 -05002668 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002669 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002670 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002671 SkScalar innerRadius = rrect.fType != kFill_RRectType
2672 ? rrect.fInnerRadius / rrect.fOuterRadius
2673 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002674 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002675 verts.write(bounds.fLeft, yCoords[i],
2676 color,
2677 -1.0f, yOuterRadii[i],
2678 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002679
Brian Osmana1d4eb92018-12-06 16:33:10 -05002680 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2681 color,
2682 0.0f, yOuterRadii[i],
2683 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002684
Brian Osmana1d4eb92018-12-06 16:33:10 -05002685 verts.write(bounds.fRight - outerRadius, yCoords[i],
2686 color,
2687 0.0f, yOuterRadii[i],
2688 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002689
Brian Osmana1d4eb92018-12-06 16:33:10 -05002690 verts.write(bounds.fRight, yCoords[i],
2691 color,
2692 1.0f, yOuterRadii[i],
2693 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002694 }
jvanverthc3d0e422016-08-25 08:12:35 -07002695 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002696 // Effectively this is an additional stroked rrect, with its
2697 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2698 // This will give us correct AA in the center and the correct
2699 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002700 //
jvanvertha4f1af82016-08-29 07:17:47 -07002701 // Also, the outer offset is a constant vector pointing to the right, which
2702 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002703 if (kOverstroke_RRectType == rrect.fType) {
2704 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002705
Brian Salomon05441c42017-05-15 16:45:49 -04002706 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002707 // this is the normalized distance from the outer rectangle of this
2708 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002709 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002710
Brian Osmana1d4eb92018-12-06 16:33:10 -05002711 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002712 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002713 }
jvanverth6a397612016-08-26 08:15:33 -07002714
Brian Salomon05441c42017-05-15 16:45:49 -04002715 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2716 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002717 for (int i = 0; i < primIndexCount; ++i) {
2718 *indices++ = primIndices[i] + currStartVertex;
2719 }
2720
Brian Salomon05441c42017-05-15 16:45:49 -04002721 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002722 }
2723
Robert Phillips4490d922020-03-03 14:50:59 -05002724 fMesh = target->allocMesh();
2725 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002726 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002727 }
2728
2729 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002730 if (!fProgramInfo || !fMesh) {
2731 return;
2732 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002733
Chris Dalton765ed362020-03-16 17:34:44 -06002734 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2735 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2736 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002737 }
2738
Herb Derbye25c3002020-10-27 15:57:27 -04002739 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002740 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002741
2742 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002743 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002744 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002745 }
2746
Brian Salomon05441c42017-05-15 16:45:49 -04002747 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002748 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002749 }
2750
Brian Salomon05441c42017-05-15 16:45:49 -04002751 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002752 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2753 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002754 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002755 }
2756
Brian Salomon05441c42017-05-15 16:45:49 -04002757 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002758 fVertCount += that->fVertCount;
2759 fIndexCount += that->fIndexCount;
2760 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002761 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002762 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002763 }
2764
John Stilesaf366522020-08-13 09:57:34 -04002765#if GR_TEST_UTILS
2766 SkString onDumpInfo() const override {
2767 SkString string;
2768 for (int i = 0; i < fRRects.count(); ++i) {
2769 string.appendf(
2770 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2771 "InnerRad: %.2f, OuterRad: %.2f\n",
2772 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2773 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2774 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2775 fRRects[i].fOuterRadius);
2776 }
2777 string += fHelper.dumpInfo();
2778 return string;
2779 }
2780#endif
2781
Brian Salomon05441c42017-05-15 16:45:49 -04002782 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002783 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002784 SkScalar fInnerRadius;
2785 SkScalar fOuterRadius;
2786 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002787 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002788 };
2789
Brian Salomon289e3d82016-12-14 15:52:56 -05002790 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002791 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002792 int fVertCount;
2793 int fIndexCount;
2794 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002795 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002796 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002797
Chris Daltoneb694b72020-03-16 09:25:50 -06002798 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002799 GrProgramInfo* fProgramInfo = nullptr;
2800
John Stiles7571f9e2020-09-02 22:42:33 -04002801 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002802};
2803
jvanverth84839f62016-08-29 10:16:40 -07002804static const int kNumRRectsInIndexBuffer = 256;
2805
2806GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2807GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002808static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2809 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002810 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2811 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2812 switch (type) {
2813 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002814 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002815 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2816 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002817 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002818 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002819 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2820 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002821 default:
2822 SkASSERT(false);
2823 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002824 }
jvanverth84839f62016-08-29 10:16:40 -07002825}
2826
Brian Salomon05441c42017-05-15 16:45:49 -04002827class EllipticalRRectOp : public GrMeshDrawOp {
2828private:
2829 using Helper = GrSimpleMeshDrawOpHelper;
2830
joshualitt76e7fb62015-02-11 08:52:27 -08002831public:
Brian Salomon25a88092016-12-01 09:36:50 -05002832 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002833
bsalomon4b4a7cc2016-07-08 04:42:54 -07002834 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2835 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002836 static GrOp::Owner Make(GrRecordingContext* context,
2837 GrPaint&& paint,
2838 const SkMatrix& viewMatrix,
2839 const SkRect& devRect,
2840 float devXRadius,
2841 float devYRadius,
2842 SkVector devStrokeWidths,
2843 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002844 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2845 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002846 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2847 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002848 if (devStrokeWidths.fX > 0) {
2849 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2850 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2851 } else {
2852 devStrokeWidths.scale(SK_ScalarHalf);
2853 }
joshualitt76e7fb62015-02-11 08:52:27 -08002854
bsalomon4b4a7cc2016-07-08 04:42:54 -07002855 // we only handle thick strokes for near-circular ellipses
2856 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002857 (SK_ScalarHalf * devXRadius > devYRadius ||
2858 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002859 return nullptr;
2860 }
2861
2862 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002863 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2864 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002865 return nullptr;
2866 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002867 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2868 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002869 return nullptr;
2870 }
Brian Salomon05441c42017-05-15 16:45:49 -04002871 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002872 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002873 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002874 devXRadius, devYRadius, devStrokeWidths,
2875 strokeOnly);
2876 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002877
Herb Derbyc76d4092020-10-07 16:46:15 -04002878 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002879 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2880 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002881 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002882 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002883 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002884 SkScalar innerXRadius = 0.0f;
2885 SkScalar innerYRadius = 0.0f;
2886 SkRect bounds = devRect;
2887 bool stroked = false;
2888 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002889 // this is legit only if scale & translation (which should be the case at the moment)
2890 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002891 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2892 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2894 }
2895
Brian Salomon05441c42017-05-15 16:45:49 -04002896 devXRadius += devStrokeHalfWidths.fX;
2897 devYRadius += devStrokeHalfWidths.fY;
2898 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002899 }
2900
Brian Salomon05441c42017-05-15 16:45:49 -04002901 fStroked = stroked;
2902 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002903 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002904 // Expand the rect for aa in order to generate the correct vertices.
2905 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002906 fRRects.emplace_back(
2907 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002908 }
2909
Brian Salomon289e3d82016-12-14 15:52:56 -05002910 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002911
Chris Dalton1706cbf2019-05-21 19:35:29 -06002912 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002913 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002914 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002915 } else {
2916 fHelper.visitProxies(func);
2917 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002918 }
2919
Chris Dalton6ce447a2019-06-23 18:07:38 -06002920 GrProcessorSet::Analysis finalize(
2921 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2922 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002923 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002924 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002925 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002926 GrProcessorAnalysisCoverage::kSingleChannel, color,
2927 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002928 }
2929
2930 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2931
bsalomone46f9fe2015-08-18 06:05:14 -07002932private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002933 GrProgramInfo* programInfo() override { return fProgramInfo; }
2934
Robert Phillips4133dc42020-03-11 15:55:55 -04002935 void onCreateProgramInfo(const GrCaps* caps,
2936 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002937 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002938 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002939 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002940 GrXferBarrierFlags renderPassXferBarriers,
2941 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002942 SkMatrix localMatrix;
2943 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002944 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002945 }
2946
Robert Phillips4490d922020-03-03 14:50:59 -05002947 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2948 fUseScale, localMatrix);
2949
Brian Salomon8afde5f2020-04-01 16:22:00 -04002950 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002951 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002952 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002953 }
2954
Robert Phillips4490d922020-03-03 14:50:59 -05002955 void onPrepareDraws(Target* target) override {
2956 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002957 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002958 if (!fProgramInfo) {
2959 return;
2960 }
2961 }
joshualitt76e7fb62015-02-11 08:52:27 -08002962
bsalomonb5238a72015-05-05 07:49:49 -07002963 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002964 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002965 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2966 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002967
Brian Salomon12d22642019-01-29 14:38:50 -05002968 if (!indexBuffer) {
2969 SkDebugf("Could not allocate indices\n");
2970 return;
2971 }
Robert Phillips4490d922020-03-03 14:50:59 -05002972 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2973 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002974 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002975 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002976 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002977 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002978 SkDebugf("Could not allocate vertices\n");
2979 return;
2980 }
2981
Brian Salomon05441c42017-05-15 16:45:49 -04002982 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002983 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002984 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002985 float reciprocalRadii[4] = {
2986 SkScalarInvert(rrect.fXRadius),
2987 SkScalarInvert(rrect.fYRadius),
2988 SkScalarInvert(rrect.fInnerXRadius),
2989 SkScalarInvert(rrect.fInnerYRadius)
2990 };
joshualitt76e7fb62015-02-11 08:52:27 -08002991
Brian Osmane3afdd52020-10-28 10:49:56 -04002992 // If the stroke width is exactly double the radius, the inner radii will be zero.
2993 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
2994 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
2995 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
2996
joshualitt76e7fb62015-02-11 08:52:27 -08002997 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00002998 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2999 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003000
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003001 SkScalar xMaxOffset = xOuterRadius;
3002 SkScalar yMaxOffset = yOuterRadius;
3003 if (!fStroked) {
3004 // For filled rrects we map a unit circle in the vertex attributes rather than
3005 // computing an ellipse and modifying that distance, so we normalize to 1.
3006 xMaxOffset /= rrect.fXRadius;
3007 yMaxOffset /= rrect.fYRadius;
3008 }
3009
Brian Salomon05441c42017-05-15 16:45:49 -04003010 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003011
Brian Salomon289e3d82016-12-14 15:52:56 -05003012 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3013 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003014 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003015 SK_ScalarNearlyZero, // we're using inversesqrt() in
3016 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003017 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003018
Brian Osman788b9162020-02-07 10:36:46 -05003019 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003020 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003021 verts.write(bounds.fLeft, yCoords[i],
3022 color,
3023 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003024 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003025 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003026
Brian Osmana1d4eb92018-12-06 16:33:10 -05003027 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3028 color,
3029 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003030 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003031 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003032
Brian Osmana1d4eb92018-12-06 16:33:10 -05003033 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3034 color,
3035 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003036 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003037 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003038
Brian Osmana1d4eb92018-12-06 16:33:10 -05003039 verts.write(bounds.fRight, yCoords[i],
3040 color,
3041 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003042 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003043 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003044 }
3045 }
Robert Phillips4490d922020-03-03 14:50:59 -05003046 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003047 }
3048
3049 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003050 if (!fProgramInfo || !fMesh) {
3051 return;
3052 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003053
Chris Dalton765ed362020-03-16 17:34:44 -06003054 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3055 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3056 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003057 }
3058
Herb Derbye25c3002020-10-27 15:57:27 -04003059 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003060 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003061
Brian Salomon05441c42017-05-15 16:45:49 -04003062 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003063 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003064 }
3065
bsalomoncdaa97b2016-03-08 08:30:14 -08003066 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003067 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003068 }
3069
Brian Salomon05441c42017-05-15 16:45:49 -04003070 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003071 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3072 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003073 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003074 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003075
Brian Salomon05441c42017-05-15 16:45:49 -04003076 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003077 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003078 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003079 }
3080
John Stilesaf366522020-08-13 09:57:34 -04003081#if GR_TEST_UTILS
3082 SkString onDumpInfo() const override {
3083 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3084 for (const auto& geo : fRRects) {
3085 string.appendf(
3086 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3087 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3088 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3089 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3090 geo.fInnerXRadius, geo.fInnerYRadius);
3091 }
3092 string += fHelper.dumpInfo();
3093 return string;
3094 }
3095#endif
3096
Brian Salomon05441c42017-05-15 16:45:49 -04003097 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003098 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003099 SkScalar fXRadius;
3100 SkScalar fYRadius;
3101 SkScalar fInnerXRadius;
3102 SkScalar fInnerYRadius;
3103 SkRect fDevBounds;
3104 };
3105
Brian Salomon289e3d82016-12-14 15:52:56 -05003106 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003107 Helper fHelper;
3108 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003109 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003110 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003111 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003112
Chris Daltoneb694b72020-03-16 09:25:50 -06003113 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003114 GrProgramInfo* fProgramInfo = nullptr;
3115
John Stiles7571f9e2020-09-02 22:42:33 -04003116 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003117};
3118
Herb Derbyc76d4092020-10-07 16:46:15 -04003119GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3120 GrPaint&& paint,
3121 const SkMatrix& viewMatrix,
3122 const SkRRect& rrect,
3123 const SkStrokeRec& stroke,
3124 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003125 SkASSERT(viewMatrix.rectStaysRect());
3126 SkASSERT(viewMatrix.isSimilarity());
3127 SkASSERT(rrect.isSimple());
3128 SkASSERT(!rrect.isOval());
3129 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3130
3131 // RRect ops only handle simple, but not too simple, rrects.
3132 // Do any matrix crunching before we reset the draw state for device coords.
3133 const SkRect& rrectBounds = rrect.getBounds();
3134 SkRect bounds;
3135 viewMatrix.mapRect(&bounds, rrectBounds);
3136
3137 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3138 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3139 viewMatrix[SkMatrix::kMSkewY]));
3140
3141 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3142 SkScalar scaledStroke = -1;
3143 SkScalar strokeWidth = stroke.getWidth();
3144 SkStrokeRec::Style style = stroke.getStyle();
3145
3146 bool isStrokeOnly =
3147 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3148 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3149
3150 if (hasStroke) {
3151 if (SkStrokeRec::kHairline_Style == style) {
3152 scaledStroke = SK_Scalar1;
3153 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003154 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3155 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003156 }
3157 }
3158
3159 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3160 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3161 // patch will have fractional coverage. This only matters when the interior is actually filled.
3162 // We could consider falling back to rect rendering here, since a tiny radius is
3163 // indistinguishable from a square corner.
3164 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3165 return nullptr;
3166 }
3167
3168 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3169 scaledStroke, isStrokeOnly);
3170}
3171
Herb Derbyc76d4092020-10-07 16:46:15 -04003172GrOp::Owner make_rrect_op(GrRecordingContext* context,
3173 GrPaint&& paint,
3174 const SkMatrix& viewMatrix,
3175 const SkRRect& rrect,
3176 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003177 SkASSERT(viewMatrix.rectStaysRect());
3178 SkASSERT(rrect.isSimple());
3179 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003180
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003181 // RRect ops only handle simple, but not too simple, rrects.
3182 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003183 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003184 SkRect bounds;
3185 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003186
Mike Reed242135a2018-02-22 13:41:39 -05003187 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003188 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3189 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3190 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3191 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003192
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003194
bsalomon4b4a7cc2016-07-08 04:42:54 -07003195 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3196 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003198
Brian Salomon289e3d82016-12-14 15:52:56 -05003199 bool isStrokeOnly =
3200 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003201 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3202
3203 if (hasStroke) {
3204 if (SkStrokeRec::kHairline_Style == style) {
3205 scaledStroke.set(1, 1);
3206 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003207 scaledStroke.fX = SkScalarAbs(
3208 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3209 scaledStroke.fY = SkScalarAbs(
3210 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003211 }
3212
Jim Van Verth64b85892019-06-17 12:01:46 -04003213 // if half of strokewidth is greater than radius, we don't handle that right now
3214 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3215 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003216 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003217 }
3218 }
3219
Brian Salomon8a97f562019-04-18 14:07:27 -04003220 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003221 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003222 std::swap(xRadius, yRadius);
3223 std::swap(scaledStroke.fX, scaledStroke.fY);
3224 }
3225
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003226 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3227 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3228 // patch will have fractional coverage. This only matters when the interior is actually filled.
3229 // We could consider falling back to rect rendering here, since a tiny radius is
3230 // indistinguishable from a square corner.
3231 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003232 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003233 }
3234
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003235 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003236 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3237 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003238}
3239
Herb Derbyc76d4092020-10-07 16:46:15 -04003240GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3241 GrPaint&& paint,
3242 const SkMatrix& viewMatrix,
3243 const SkRRect& rrect,
3244 const SkStrokeRec& stroke,
3245 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003246 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003247 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003248 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003249 }
3250
3251 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003252 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003253 }
3254
Greg Daniel2655ede2019-04-10 00:49:28 +00003255 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003256}
joshualitt3e708c52015-04-30 13:49:27 -07003257
bsalomon4b4a7cc2016-07-08 04:42:54 -07003258///////////////////////////////////////////////////////////////////////////////
3259
Herb Derbyc76d4092020-10-07 16:46:15 -04003260GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3261 GrPaint&& paint,
3262 const SkMatrix& viewMatrix,
3263 const SkRect& oval,
3264 const GrStyle& style,
3265 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003266 SkScalar width = oval.width();
3267 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3268 circle_stays_circle(viewMatrix));
3269
3270 auto r = width / 2.f;
3271 SkPoint center = { oval.centerX(), oval.centerY() };
3272 if (style.hasNonDashPathEffect()) {
3273 return nullptr;
3274 } else if (style.isDashed()) {
3275 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3276 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3277 return nullptr;
3278 }
3279 auto onInterval = style.dashIntervals()[0];
3280 auto offInterval = style.dashIntervals()[1];
3281 if (offInterval == 0) {
3282 GrStyle strokeStyle(style.strokeRec(), nullptr);
3283 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3284 strokeStyle, shaderCaps);
3285 } else if (onInterval == 0) {
3286 // There is nothing to draw but we have no way to indicate that here.
3287 return nullptr;
3288 }
3289 auto angularOnInterval = onInterval / r;
3290 auto angularOffInterval = offInterval / r;
3291 auto phaseAngle = style.dashPhase() / r;
3292 // Currently this function doesn't accept ovals with different start angles, though
3293 // it could.
3294 static const SkScalar kStartAngle = 0.f;
3295 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3296 style.strokeRec().getWidth(), kStartAngle,
3297 angularOnInterval, angularOffInterval, phaseAngle);
3298 }
3299 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3300}
3301
Herb Derbyc76d4092020-10-07 16:46:15 -04003302GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3303 GrPaint&& paint,
3304 const SkMatrix& viewMatrix,
3305 const SkRect& oval,
3306 const GrStyle& style,
3307 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003308 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003309 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003310 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3311 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003312 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003313 }
3314
3315 if (style.pathEffect()) {
3316 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003317 }
3318
Stan Ilieveb868aa2017-02-21 11:06:16 -05003319 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003320 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003321 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003322 }
3323
Stan Ilieveb868aa2017-02-21 11:06:16 -05003324 // Otherwise, if we have shader derivative support, render as device-independent
3325 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003326 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3327 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3328 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3329 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3330 // Check for near-degenerate matrix
3331 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003332 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003333 style.strokeRec());
3334 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003335 }
3336
bsalomon4b4a7cc2016-07-08 04:42:54 -07003337 return nullptr;
3338}
3339
3340///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003341
Herb Derbyc76d4092020-10-07 16:46:15 -04003342GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3343 GrPaint&& paint,
3344 const SkMatrix& viewMatrix,
3345 const SkRect& oval, SkScalar startAngle,
3346 SkScalar sweepAngle, bool useCenter,
3347 const GrStyle& style,
3348 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003349 SkASSERT(!oval.isEmpty());
3350 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003351 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003352 if (SkScalarAbs(sweepAngle) >= 360.f) {
3353 return nullptr;
3354 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003355 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3356 return nullptr;
3357 }
3358 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003359 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3360 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003361 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003362 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003363}
3364
3365///////////////////////////////////////////////////////////////////////////////
3366
Hal Canary6f6961e2017-01-31 13:50:44 -05003367#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003368
Brian Salomon05441c42017-05-15 16:45:49 -04003369GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003370 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003371 SkScalar rotate = random->nextSScalar1() * 360.f;
3372 SkScalar translateX = random->nextSScalar1() * 1000.f;
3373 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003374 SkScalar scale;
3375 do {
3376 scale = random->nextSScalar1() * 100.f;
3377 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003378 SkMatrix viewMatrix;
3379 viewMatrix.setRotate(rotate);
3380 viewMatrix.postTranslate(translateX, translateY);
3381 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003382 SkRect circle = GrTest::TestSquare(random);
3383 SkPoint center = {circle.centerX(), circle.centerY()};
3384 SkScalar radius = circle.width() / 2.f;
3385 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003386 CircleOp::ArcParams arcParamsTmp;
3387 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003388 if (random->nextBool()) {
3389 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003390 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3391 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 arcParams = &arcParamsTmp;
3393 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003394 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3395 center, radius,
3396 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003397 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003398 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003399 }
Mike Klein16885072018-12-11 09:54:31 -05003400 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003401 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003402}
3403
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003404GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3405 SkScalar rotate = random->nextSScalar1() * 360.f;
3406 SkScalar translateX = random->nextSScalar1() * 1000.f;
3407 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003408 SkScalar scale;
3409 do {
3410 scale = random->nextSScalar1() * 100.f;
3411 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003412 SkMatrix viewMatrix;
3413 viewMatrix.setRotate(rotate);
3414 viewMatrix.postTranslate(translateX, translateY);
3415 viewMatrix.postScale(scale, scale);
3416 SkRect circle = GrTest::TestSquare(random);
3417 SkPoint center = {circle.centerX(), circle.centerY()};
3418 SkScalar radius = circle.width() / 2.f;
3419 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3420 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3421 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3422 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3423 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003424 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3425 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003426 startAngle, onAngle, offAngle, phase);
3427}
3428
Brian Salomon05441c42017-05-15 16:45:49 -04003429GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003430 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003431 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003432 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003433 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003434}
3435
Brian Salomon05441c42017-05-15 16:45:49 -04003436GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003437 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003438 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003439 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003440 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003441}
3442
Jim Van Verth64b85892019-06-17 12:01:46 -04003443GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3444 do {
3445 SkScalar rotate = random->nextSScalar1() * 360.f;
3446 SkScalar translateX = random->nextSScalar1() * 1000.f;
3447 SkScalar translateY = random->nextSScalar1() * 1000.f;
3448 SkScalar scale;
3449 do {
3450 scale = random->nextSScalar1() * 100.f;
3451 } while (scale == 0);
3452 SkMatrix viewMatrix;
3453 viewMatrix.setRotate(rotate);
3454 viewMatrix.postTranslate(translateX, translateY);
3455 viewMatrix.postScale(scale, scale);
3456 SkRect rect = GrTest::TestRect(random);
3457 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3458 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3459 if (rrect.isOval()) {
3460 continue;
3461 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003462 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003463 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3464 GrTest::TestStrokeRec(random), nullptr);
3465 if (op) {
3466 return op;
3467 }
3468 assert_alive(paint);
3469 } while (true);
3470}
3471
Brian Salomon05441c42017-05-15 16:45:49 -04003472GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003473 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003474 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003475 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003476 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003477}
3478
3479#endif