blob: 2d20e8cb72f0bfd2f262030976d3b5ed9beb608c [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 {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400119 const CircleGeometryProcessor& cgp = args.fGeomProc.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
John Stiles4d7ac492021-03-09 20:16:43 -0500155 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500156 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800157
joshualittabb52a12015-01-13 15:02:10 -0800158 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500159 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400160 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
161 cgp.fInPosition.asShaderVar(), cgp.fLocalMatrix,
162 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800163
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400164 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500165 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000166 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800167 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500168 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500169 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000170 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800171 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000172 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000173
Brian Salomon92be2f72018-06-19 14:33:47 -0400174 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500175 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000176 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
177 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400178 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500179 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000180 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
181 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400183 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500184 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000185 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
186 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 }
188 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400189 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400190 // We compute coverage of the round caps as circles at the butt caps produced
191 // by the clip planes. The inverse of the clip planes is applied so that there
192 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400193 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500194 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
195 " roundCapCenters.xy)));"
196 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
197 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400198 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400199 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
200 capRadius.fsIn(), capRadius.fsIn());
201 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700202 }
John Stiles4d7ac492021-03-09 20:16:43 -0500203 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204 }
205
robertphillips46d36f02015-01-18 08:14:14 -0800206 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500207 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700208 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800209 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500210 b->addBool(cgp.fStroke, "stroked");
211 b->addBool(cgp.fInClipPlane.isInitialized(), "clipPlane");
212 b->addBool(cgp.fInIsectPlane.isInitialized(), "isectPlane");
213 b->addBool(cgp.fInUnionPlane.isInitialized(), "unionPlane");
214 b->addBool(cgp.fInRoundCapCenters.isInitialized(), "roundCapCenters");
215 b->addBits(kMatrixKeyBits, ComputeMatrixKey(cgp.fLocalMatrix), "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 }
217
Brian Osman609f1592020-07-01 15:14:39 -0400218 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400219 const GrGeometryProcessor& geomProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400220 this->setTransform(pdman, fLocalMatrixUniform,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400221 geomProc.cast<CircleGeometryProcessor>().fLocalMatrix,
Michael Ludwig553db622020-06-19 10:47:30 -0400222 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700223 }
224
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400226 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400227
228 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
229 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230 };
231
Brian Salomon289e3d82016-12-14 15:52:56 -0500232 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400233
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500234 Attribute fInPosition;
235 Attribute fInColor;
236 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400237 // Optional attributes.
238 Attribute fInClipPlane;
239 Attribute fInIsectPlane;
240 Attribute fInUnionPlane;
241 Attribute fInRoundCapCenters;
242
Brian Salomon289e3d82016-12-14 15:52:56 -0500243 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400244 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245
John Stiles7571f9e2020-09-02 22:42:33 -0400246 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247};
248
bsalomoncdaa97b2016-03-08 08:30:14 -0800249GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250
Hal Canary6f6961e2017-01-31 13:50:44 -0500251#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500252GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400253 bool stroke = d->fRandom->nextBool();
254 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500255 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400256 bool clipPlane = d->fRandom->nextBool();
257 bool isectPlane = d->fRandom->nextBool();
258 bool unionPlane = d->fRandom->nextBool();
259 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500260 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
261 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000262}
Hal Canary6f6961e2017-01-31 13:50:44 -0500263#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400265class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
266public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500267 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
268 const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600269 return arena->make([&](void* ptr) {
270 return new (ptr) ButtCapDashedCircleGeometryProcessor(wideColor, localMatrix);
271 });
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400272 }
273
274 ~ButtCapDashedCircleGeometryProcessor() override {}
275
276 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
277
278 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
279 GLSLProcessor::GenKey(*this, caps, b);
280 }
281
282 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
283 return new GLSLProcessor();
284 }
285
286private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500287 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
288 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
289 , fLocalMatrix(localMatrix) {
290 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
291 fInColor = MakeColorAttribute("inColor", wideColor);
292 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
293 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 this->setVertexAttributes(&fInPosition, 4);
295 }
296
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400297 class GLSLProcessor : public GrGLSLGeometryProcessor {
298 public:
299 GLSLProcessor() {}
300
301 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
302 const ButtCapDashedCircleGeometryProcessor& bcscgp =
Robert Phillips787fd9d2021-03-22 14:48:09 -0400303 args.fGeomProc.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400304 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
305 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
306 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
307 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
308
309 // emit attributes
310 varyingHandler->emitAttributes(bcscgp);
311 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500312 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400313
314 fragBuilder->codeAppend("float4 dashParams;");
315 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500316 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400317 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
318 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
319 varyingHandler->addVarying("wrapDashes", &wrapDashes,
320 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
321 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
322 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
323 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500324 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400325 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
326 // x = length of on interval, y = length of on + off.
327 // There are two other parameters in dashParams.zw:
328 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
329 // Each interval has a "corresponding" dash which may be shifted partially or
330 // fully out of its interval by the phase. So there may be up to two "visual"
331 // dashes in an interval.
332 // When computing coverage in an interval we look at three dashes. These are the
333 // "corresponding" dashes from the current, previous, and next intervals. Any of these
334 // may be phase shifted into our interval or even when phase=0 they may be within half a
335 // pixel distance of a pixel center in the interval.
336 // When in the first interval we need to check the dash from the last interval. And
337 // similarly when in the last interval we need to check the dash from the first
338 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
339 // We compute the dash begin/end angles in the vertex shader and apply them in the
340 // fragment shader when we detect we're in the first/last interval.
341 vertBuilder->codeAppend(R"(
342 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
343 // to the fragment shader as a varying.
344 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500345 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400346 // We can happen to be perfectly divisible.
347 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500348 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400349 }
350 // Let 'l' be the last interval before reaching 2 pi.
351 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
352 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
353 // interval.
354 half offset = 0;
355 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500356 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400357 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500358 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400359 }
360 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
361 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
362 // min.
363 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
364
365 // Based on the phase determine whether the -1st, 0th, or 1st interval's
366 // "corresponding" dash appears in the 0th interval and is closest to l.
367 offset = 0;
368 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500369 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400370 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500371 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400372 }
373 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
374 wrapDashes.w = wrapDashes.z + dashParams.x;
375 // The start of the dash we're considering may be clipped by the start of the
376 // circle.
377 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
378 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500379 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400380 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
381 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
382 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
383
384 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500385 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400386 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500387 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
389
390 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500391 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400392 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
393 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
394 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
John Stiles6b58a332020-10-26 17:53:06 -0400400 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
401 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
John Stilesfdf61482020-10-27 09:45:40 -0400402 {fnArgs, SK_ARRAY_COUNT(fnArgs)}, R"(
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400406 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400407 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 fragBuilder->codeAppend(R"(
409 float d = length(circleEdge.xy) * circleEdge.z;
410
411 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500412 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400413 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500414 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400415 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 edgeAlpha *= innerAlpha;
417
Ethan Nicholase1f55022019-02-05 17:17:40 -0500418 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400419 angleFromStart = mod(angleFromStart, 6.28318530718);
420 float x = mod(angleFromStart, dashParams.y);
421 // Convert the radial distance from center to pixel into a diameter.
422 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500423 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
424 half(dashParams.w));
425 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
426 half(dashParams.y) + half(dashParams.x) -
427 half(dashParams.w));
428 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
429 half(-dashParams.y) + half(dashParams.x) -
430 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400431 half dashAlpha = 0;
432 )");
433 fragBuilder->codeAppendf(R"(
434 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500435 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 currDash.y = min(currDash.y, lastIntervalLength);
437 if (nextDash.x >= lastIntervalLength) {
438 // The next dash is outside the 0..2pi range, throw it away
439 nextDash.xy = half2(1000);
440 } else {
441 // Clip the end of the next dash to the end of the circle
442 nextDash.y = min(nextDash.y, lastIntervalLength);
443 }
444 }
445 )", fnName.c_str(), fnName.c_str());
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.x = max(currDash.x, 0);
450 if (prevDash.y <= 0) {
451 // The previous dash is outside the 0..2pi range, throw it away
452 prevDash.xy = half2(1000);
453 } else {
454 // Clip the start previous dash to the start of the circle
455 prevDash.x = max(prevDash.x, 0);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500460 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
461 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
462 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400463 dashAlpha = min(dashAlpha, 1);
464 edgeAlpha *= dashAlpha;
465 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
466 fnName.c_str());
John Stiles4d7ac492021-03-09 20:16:43 -0500467 fragBuilder->codeAppendf("half4 %s = half4(edgeAlpha);", args.fOutputCoverage);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400468 }
469
470 static void GenKey(const GrGeometryProcessor& gp,
471 const GrShaderCaps&,
472 GrProcessorKeyBuilder* b) {
473 const ButtCapDashedCircleGeometryProcessor& bcscgp =
474 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500475 b->addBits(kMatrixKeyBits, ComputeMatrixKey(bcscgp.fLocalMatrix), "localMatrixType");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 }
477
Brian Osman609f1592020-07-01 15:14:39 -0400478 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400479 const GrGeometryProcessor& geomProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400480 this->setTransform(pdman, fLocalMatrixUniform,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400481 geomProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
Michael Ludwig553db622020-06-19 10:47:30 -0400482 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483 }
484
485 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400486 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400487
488 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
489 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400490 };
491
492 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500493 Attribute fInPosition;
494 Attribute fInColor;
495 Attribute fInCircleEdge;
496 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400497
498 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
499
John Stiles7571f9e2020-09-02 22:42:33 -0400500 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501};
502
503#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500504GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500505 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400506 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500507 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508}
509#endif
510
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000511///////////////////////////////////////////////////////////////////////////////
512
513/**
514 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000515 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
516 * in both x and y directions.
517 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000518 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000519 */
520
bsalomoncdaa97b2016-03-08 08:30:14 -0800521class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000522public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500523 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
524 bool useScale, const SkMatrix& localMatrix) {
Mike Kleinf1241082020-12-14 15:59:09 -0600525 return arena->make([&](void* ptr) {
526 return new (ptr) EllipseGeometryProcessor(stroke, wideColor, useScale, localMatrix);
527 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500528 }
529
530 ~EllipseGeometryProcessor() override {}
531
532 const char* name() const override { return "EllipseGeometryProcessor"; }
533
534 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
535 GLSLProcessor::GenKey(*this, caps, b);
536 }
537
538 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
539 return new GLSLProcessor();
540 }
541
542private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000543 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400544 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500545 : INHERITED(kEllipseGeometryProcessor_ClassID)
546 , fLocalMatrix(localMatrix)
547 , fStroke(stroke)
548 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500549 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500550 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400551 if (useScale) {
552 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
553 } else {
554 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
555 }
556 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500557 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000558 }
559
egdaniel57d3b032015-11-13 11:57:27 -0800560 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800562 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563
Brian Salomon289e3d82016-12-14 15:52:56 -0500564 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400565 const EllipseGeometryProcessor& egp = args.fGeomProc.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800566 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800567 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800568 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000569
joshualittabb52a12015-01-13 15:02:10 -0800570 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800571 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800572
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400573 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
574 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800575 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800576 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500577 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000578
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400579 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800580 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800582
Chris Dalton60283612018-02-14 13:38:14 -0700583 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700584 // setup pass through color
John Stiles4d7ac492021-03-09 20:16:43 -0500585 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500586 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800587
joshualittabb52a12015-01-13 15:02:10 -0800588 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500589 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400590 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
591 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
592 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800593
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400594 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
595 // to compute both the edges because we need two separate test equations for
596 // the single offset.
597 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
598 // the distance by the gradient, non-uniformly scaled by the inverse of the
599 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800600
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400601 // On medium precision devices, we scale the denominator of the distance equation
602 // before taking the inverse square root to minimize the chance that we're dividing
603 // by zero, then we scale the result back.
604
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000605 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400606 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400607 if (egp.fStroke) {
608 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
609 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400610 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
611 if (egp.fUseScale) {
612 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
613 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
614 } else {
615 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
616 }
617 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700618
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000619 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400620 if (args.fShaderCaps->floatIs32Bits()) {
621 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
622 } else {
623 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
624 }
625 if (egp.fUseScale) {
626 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
627 ellipseOffsets.fsIn());
628 } else {
629 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
630 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000631 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000632
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000633 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800634 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800636 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400637 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400638 if (egp.fUseScale) {
639 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
640 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
641 } else {
642 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
643 }
644 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
645 if (!args.fShaderCaps->floatIs32Bits()) {
646 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
647 }
648 if (egp.fUseScale) {
649 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
650 ellipseOffsets.fsIn());
651 } else {
652 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
653 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000654 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655 }
656
John Stiles4d7ac492021-03-09 20:16:43 -0500657 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658 }
659
robertphillips46d36f02015-01-18 08:14:14 -0800660 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500661 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700662 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800663 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500664 b->addBool(egp.fStroke, "stroked");
665 b->addBits(kMatrixKeyBits, ComputeMatrixKey(egp.fLocalMatrix), "localMatrixType");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666 }
667
Brian Osman609f1592020-07-01 15:14:39 -0400668 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400669 const GrGeometryProcessor& geomProc) override {
670 const EllipseGeometryProcessor& egp = geomProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400671 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700672 }
673
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000674 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400675 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400676
677 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
678 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679 };
680
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500681 Attribute fInPosition;
682 Attribute fInColor;
683 Attribute fInEllipseOffset;
684 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400685
joshualitte3ababe2015-05-15 07:56:07 -0700686 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000687 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400688 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400690 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691
John Stiles7571f9e2020-09-02 22:42:33 -0400692 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693};
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
Hal Canary6f6961e2017-01-31 13:50:44 -0500697#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500698GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
699 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
700 d->fRandom->nextBool(), d->fRandom->nextBool(),
701 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000702}
Hal Canary6f6961e2017-01-31 13:50:44 -0500703#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704
705///////////////////////////////////////////////////////////////////////////////
706
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000707/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000708 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000709 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
710 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
711 * using differentials.
712 *
713 * The result is device-independent and can be used with any affine matrix.
714 */
715
bsalomoncdaa97b2016-03-08 08:30:14 -0800716enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000717
bsalomoncdaa97b2016-03-08 08:30:14 -0800718class DIEllipseGeometryProcessor : public GrGeometryProcessor {
719public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500720 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
721 const SkMatrix& viewMatrix, DIEllipseStyle style) {
Mike Kleinf1241082020-12-14 15:59:09 -0600722 return arena->make([&](void* ptr) {
723 return new (ptr) DIEllipseGeometryProcessor(wideColor, useScale, viewMatrix, style);
724 });
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500725 }
726
727 ~DIEllipseGeometryProcessor() override {}
728
729 const char* name() const override { return "DIEllipseGeometryProcessor"; }
730
731 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
732 GLSLProcessor::GenKey(*this, caps, b);
733 }
734
735 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
736 return new GLSLProcessor();
737 }
738
739private:
Greg Daniel2655ede2019-04-10 00:49:28 +0000740 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400741 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400742 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400743 , fViewMatrix(viewMatrix)
744 , fUseScale(useScale)
745 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500746 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500747 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400748 if (useScale) {
749 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
750 kFloat3_GrSLType};
751 } else {
752 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
753 kFloat2_GrSLType};
754 }
755 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500756 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 }
758
egdaniel57d3b032015-11-13 11:57:27 -0800759 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500761 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762
joshualitt465283c2015-09-11 08:19:35 -0700763 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
Robert Phillips787fd9d2021-03-22 14:48:09 -0400764 const auto& diegp = args.fGeomProc.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800765 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800766 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800767 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000768
joshualittabb52a12015-01-13 15:02:10 -0800769 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800770 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800771
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400772 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
773 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800774 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500775 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700776
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400777 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800778 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800780
Chris Dalton60283612018-02-14 13:38:14 -0700781 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
John Stiles4d7ac492021-03-09 20:16:43 -0500782 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800784
joshualittabb52a12015-01-13 15:02:10 -0800785 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400786 this->writeOutputPosition(vertBuilder,
787 uniformHandler,
788 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500789 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400790 diegp.fViewMatrix,
791 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400792 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800793
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000794 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400795 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
796 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
797 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
798 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500799 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400800 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
801 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500802 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 if (diegp.fUseScale) {
804 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
805 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000806
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400807 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000808 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 if (args.fShaderCaps->floatIs32Bits()) {
810 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
811 } else {
812 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
813 }
814 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
815 if (diegp.fUseScale) {
816 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
817 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800818 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000819 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000820 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
821 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000823 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000824 }
825
826 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800827 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800828 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
829 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400830 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
831 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500832 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
834 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500835 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400836 if (diegp.fUseScale) {
837 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
838 }
839 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
840 if (!args.fShaderCaps->floatIs32Bits()) {
841 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
842 }
843 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
844 if (diegp.fUseScale) {
845 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
846 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000847 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000848 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000849
John Stiles4d7ac492021-03-09 20:16:43 -0500850 fragBuilder->codeAppendf("half4 %s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000851 }
852
robertphillips46d36f02015-01-18 08:14:14 -0800853 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500854 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700855 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800856 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Brian Osman48d7f7c2021-03-03 13:38:08 -0500857 b->addBits(2, static_cast<uint32_t>(diegp.fStyle), "style");
858 b->addBits(kMatrixKeyBits, ComputeMatrixKey(diegp.fViewMatrix), "viewMatrixType");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000859 }
860
Brian Osman609f1592020-07-01 15:14:39 -0400861 void setData(const GrGLSLProgramDataManager& pdman,
Robert Phillips787fd9d2021-03-22 14:48:09 -0400862 const GrGeometryProcessor& geomProc) override {
863 const auto& diegp = geomProc.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700864
Michael Ludwig553db622020-06-19 10:47:30 -0400865 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000866 }
867
868 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400869 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700870 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800871
John Stiles7571f9e2020-09-02 22:42:33 -0400872 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000873 };
874
Brian Salomon92be2f72018-06-19 14:33:47 -0400875
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500876 Attribute fInPosition;
877 Attribute fInColor;
878 Attribute fInEllipseOffsets0;
879 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400880
Brian Salomon289e3d82016-12-14 15:52:56 -0500881 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400882 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500883 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000884
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400885 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886
John Stiles7571f9e2020-09-02 22:42:33 -0400887 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888};
889
bsalomoncdaa97b2016-03-08 08:30:14 -0800890GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000891
Hal Canary6f6961e2017-01-31 13:50:44 -0500892#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500893GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
894 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
895 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
896 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897}
Hal Canary6f6961e2017-01-31 13:50:44 -0500898#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000899
900///////////////////////////////////////////////////////////////////////////////
901
jvanverth6ca48822016-10-07 06:57:32 -0700902// We have two possible cases for geometry for a circle:
903
904// In the case of a normal fill, we draw geometry for the circle as an octagon.
905static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500906 // enter the octagon
907 // clang-format off
908 0, 1, 8, 1, 2, 8,
909 2, 3, 8, 3, 4, 8,
910 4, 5, 8, 5, 6, 8,
911 6, 7, 8, 7, 0, 8
912 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700913};
914
915// For stroked circles, we use two nested octagons.
916static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500917 // enter the octagon
918 // clang-format off
919 0, 1, 9, 0, 9, 8,
920 1, 2, 10, 1, 10, 9,
921 2, 3, 11, 2, 11, 10,
922 3, 4, 12, 3, 12, 11,
923 4, 5, 13, 4, 13, 12,
924 5, 6, 14, 5, 14, 13,
925 6, 7, 15, 6, 15, 14,
926 7, 0, 8, 7, 8, 15,
927 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700928};
929
Brian Osman9d958b52018-11-13 12:46:56 -0500930// Normalized geometry for octagons that circumscribe and lie on a circle:
931
932static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
933static constexpr SkPoint kOctagonOuter[] = {
934 SkPoint::Make(-kOctOffset, -1),
935 SkPoint::Make( kOctOffset, -1),
936 SkPoint::Make( 1, -kOctOffset),
937 SkPoint::Make( 1, kOctOffset),
938 SkPoint::Make( kOctOffset, 1),
939 SkPoint::Make(-kOctOffset, 1),
940 SkPoint::Make(-1, kOctOffset),
941 SkPoint::Make(-1, -kOctOffset),
942};
943
944// cosine and sine of pi/8
945static constexpr SkScalar kCosPi8 = 0.923579533f;
946static constexpr SkScalar kSinPi8 = 0.382683432f;
947static constexpr SkPoint kOctagonInner[] = {
948 SkPoint::Make(-kSinPi8, -kCosPi8),
949 SkPoint::Make( kSinPi8, -kCosPi8),
950 SkPoint::Make( kCosPi8, -kSinPi8),
951 SkPoint::Make( kCosPi8, kSinPi8),
952 SkPoint::Make( kSinPi8, kCosPi8),
953 SkPoint::Make(-kSinPi8, kCosPi8),
954 SkPoint::Make(-kCosPi8, kSinPi8),
955 SkPoint::Make(-kCosPi8, -kSinPi8),
956};
Brian Salomon289e3d82016-12-14 15:52:56 -0500957
jvanverth6ca48822016-10-07 06:57:32 -0700958static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
959static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
960static const int kVertsPerStrokeCircle = 16;
961static const int kVertsPerFillCircle = 9;
962
963static int circle_type_to_vert_count(bool stroked) {
964 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
965}
966
967static int circle_type_to_index_count(bool stroked) {
968 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
969}
970
971static const uint16_t* circle_type_to_indices(bool stroked) {
972 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
973}
974
975///////////////////////////////////////////////////////////////////////////////
976
Brian Salomon05441c42017-05-15 16:45:49 -0400977class CircleOp final : public GrMeshDrawOp {
978private:
979 using Helper = GrSimpleMeshDrawOpHelper;
980
joshualitt76e7fb62015-02-11 08:52:27 -0800981public:
Brian Salomon25a88092016-12-01 09:36:50 -0500982 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700983
bsalomon4f3a0ca2016-08-22 13:14:26 -0700984 /** Optional extra params to render a partial arc rather than a full circle. */
985 struct ArcParams {
986 SkScalar fStartAngleRadians;
987 SkScalar fSweepAngleRadians;
988 bool fUseCenter;
989 };
Brian Salomon05441c42017-05-15 16:45:49 -0400990
Herb Derbyc76d4092020-10-07 16:46:15 -0400991 static GrOp::Owner Make(GrRecordingContext* context,
992 GrPaint&& paint,
993 const SkMatrix& viewMatrix,
994 SkPoint center,
995 SkScalar radius,
996 const GrStyle& style,
997 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 if (style.hasPathEffect()) {
1000 return nullptr;
1001 }
Brian Salomon05441c42017-05-15 16:45:49 -04001002 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001003 SkStrokeRec::Style recStyle = stroke.getStyle();
1004 if (arcParams) {
1005 // Arc support depends on the style.
1006 switch (recStyle) {
1007 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001008 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001009 return nullptr;
1010 case SkStrokeRec::kFill_Style:
1011 // This supports all fills.
1012 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001013 case SkStrokeRec::kStroke_Style:
1014 // Strokes that don't use the center point are supported with butt and round
1015 // caps.
1016 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1017 return nullptr;
1018 }
1019 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001021 // Hairline only supports butt cap. Round caps could be emulated by slightly
1022 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001023 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1024 return nullptr;
1025 }
1026 break;
1027 }
1028 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001029 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1030 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001031 }
1032
Herb Derbyc76d4092020-10-07 16:46:15 -04001033 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001034 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1035 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001036 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001037 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001038 const SkStrokeRec& stroke = style.strokeRec();
1039 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001040
Brian Salomon45c92202018-04-10 10:53:58 -04001041 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001042
bsalomon4b4a7cc2016-07-08 04:42:54 -07001043 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001044 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001045 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001046
Brian Salomon289e3d82016-12-14 15:52:56 -05001047 bool isStrokeOnly =
1048 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001050
jvanverth6ca48822016-10-07 06:57:32 -07001051 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001052 SkScalar outerRadius = radius;
1053 SkScalar halfWidth = 0;
1054 if (hasStroke) {
1055 if (SkScalarNearlyZero(strokeWidth)) {
1056 halfWidth = SK_ScalarHalf;
1057 } else {
1058 halfWidth = SkScalarHalf(strokeWidth);
1059 }
1060
1061 outerRadius += halfWidth;
1062 if (isStrokeOnly) {
1063 innerRadius = radius - halfWidth;
1064 }
1065 }
1066
1067 // The radii are outset for two reasons. First, it allows the shader to simply perform
1068 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1069 // Second, the outer radius is used to compute the verts of the bounding box that is
1070 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001071 outerRadius += SK_ScalarHalf;
1072 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001073 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001074 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001075
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 // This makes every point fully inside the intersection plane.
1077 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1078 // This makes every point fully outside the union plane.
1079 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001080 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001081 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1082 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001083 if (arcParams) {
1084 // The shader operates in a space where the circle is translated to be centered at the
1085 // origin. Here we compute points on the unit circle at the starting and ending angles.
1086 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001087 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1088 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001089 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001090 stopPoint.fY = SkScalarSin(endAngle);
1091 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001092
1093 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1094 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1095 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1096 startPoint.normalize();
1097 stopPoint.normalize();
1098
Brian Salomon3517aa72019-12-11 08:16:22 -05001099 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1100 // should orient the clip planes for arcs.
1101 SkASSERT(viewMatrix.isSimilarity());
1102 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1103 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1104 if (upperLeftDet < 0) {
1105 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001106 }
1107
Brian Salomon45c92202018-04-10 10:53:58 -04001108 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1109 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1110 SkPoint roundCaps[2];
1111 if (fRoundCaps) {
1112 // Compute the cap center points in the normalized space.
1113 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1114 roundCaps[0] = startPoint * midRadius;
1115 roundCaps[1] = stopPoint * midRadius;
1116 } else {
1117 roundCaps[0] = kUnusedRoundCaps[0];
1118 roundCaps[1] = kUnusedRoundCaps[1];
1119 }
1120
bsalomon4f3a0ca2016-08-22 13:14:26 -07001121 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001122 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1123 // center of the butts.
1124 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001126 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001128 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1129 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1130 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001131 if (useCenter) {
1132 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1133 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001134 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1135 if (arcParams->fSweepAngleRadians < 0) {
1136 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001137 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001138 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001139 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001140 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001141 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 color,
1143 innerRadius,
1144 outerRadius,
1145 {norm0.fX, norm0.fY, 0.5f},
1146 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1147 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001148 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001149 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001150 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001151 fClipPlaneIsect = false;
1152 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001153 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001154 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001155 color,
1156 innerRadius,
1157 outerRadius,
1158 {norm0.fX, norm0.fY, 0.5f},
1159 {norm1.fX, norm1.fY, 0.5f},
1160 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001161 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001162 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001163 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001164 fClipPlaneIsect = true;
1165 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001166 }
1167 } else {
1168 // We clip to a secant of the original circle.
1169 startPoint.scale(radius);
1170 stopPoint.scale(radius);
1171 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1172 norm.normalize();
1173 if (arcParams->fSweepAngleRadians > 0) {
1174 norm.negate();
1175 }
1176 SkScalar d = -norm.dot(startPoint) + 0.5f;
1177
Brian Salomon05441c42017-05-15 16:45:49 -04001178 fCircles.emplace_back(
1179 Circle{color,
1180 innerRadius,
1181 outerRadius,
1182 {norm.fX, norm.fY, d},
1183 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1184 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001185 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001186 devBounds,
1187 stroked});
1188 fClipPlane = true;
1189 fClipPlaneIsect = false;
1190 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001191 }
1192 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fCircles.emplace_back(
1194 Circle{color,
1195 innerRadius,
1196 outerRadius,
1197 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001200 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001201 devBounds,
1202 stroked});
1203 fClipPlane = false;
1204 fClipPlaneIsect = false;
1205 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001206 }
bsalomon88cf17d2016-07-08 06:40:56 -07001207 // Use the original radius and stroke radius for the bounds so that it does not include the
1208 // AA bloat.
1209 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001210 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001211 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001212 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001213 fVertCount = circle_type_to_vert_count(stroked);
1214 fIndexCount = circle_type_to_index_count(stroked);
1215 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001216 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001217
Brian Salomon289e3d82016-12-14 15:52:56 -05001218 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001219
Chris Dalton1706cbf2019-05-21 19:35:29 -06001220 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001221 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001222 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001223 } else {
1224 fHelper.visitProxies(func);
1225 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001226 }
1227
Chris Dalton6ce447a2019-06-23 18:07:38 -06001228 GrProcessorSet::Analysis finalize(
1229 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1230 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001231 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001232 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001233 GrProcessorAnalysisCoverage::kSingleChannel, color,
1234 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001235 }
1236
1237 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1238
bsalomone46f9fe2015-08-18 06:05:14 -07001239private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001240 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001241
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 void onCreateProgramInfo(const GrCaps* caps,
1243 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001244 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001245 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001246 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001247 GrXferBarrierFlags renderPassXferBarriers,
1248 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 SkMatrix localMatrix;
1250 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001251 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001252 }
1253
Robert Phillips4490d922020-03-03 14:50:59 -05001254 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001255 fClipPlaneIsect, fClipPlaneUnion,
1256 fRoundCaps, fWideColor,
1257 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001258
Brian Salomon8afde5f2020-04-01 16:22:00 -04001259 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001260 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001261 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001262 }
1263
Robert Phillips4490d922020-03-03 14:50:59 -05001264 void onPrepareDraws(Target* target) override {
1265 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001266 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001267 if (!fProgramInfo) {
1268 return;
1269 }
1270 }
1271
Brian Salomon12d22642019-01-29 14:38:50 -05001272 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001273 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001274 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001275 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001276 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001277 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001278 return;
1279 }
1280
Brian Salomon12d22642019-01-29 14:38:50 -05001281 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001282 int firstIndex = 0;
1283 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1284 if (!indices) {
1285 SkDebugf("Could not allocate indices\n");
1286 return;
1287 }
1288
1289 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001290 for (const auto& circle : fCircles) {
1291 SkScalar innerRadius = circle.fInnerRadius;
1292 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001293 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001294 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001295
joshualitt76e7fb62015-02-11 08:52:27 -08001296 // The inner radius in the vertex data must be specified in normalized space.
1297 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001298 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001299
1300 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001301 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001302
Brian Osman9a24fee2018-08-03 09:48:42 -04001303 SkVector geoClipPlane = { 0, 0 };
1304 SkScalar offsetClipDist = SK_Scalar1;
1305 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1306 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1307 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1308 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1309 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1310 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1311 // the AA can extend just past the center of the circle.
1312 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1313 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1314 SkAssertResult(geoClipPlane.normalize());
1315 offsetClipDist = 0.5f / halfWidth;
1316 }
1317
Brian Osman7d8f82b2018-11-08 10:24:09 -05001318 for (int i = 0; i < 8; ++i) {
1319 // This clips the normalized offset to the half-plane we computed above. Then we
1320 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001321 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001322 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001323 vertices.write(center + offset * halfWidth,
1324 color,
1325 offset,
1326 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001327 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001328 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001329 }
1330 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001331 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001332 }
1333 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001334 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001335 }
1336 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001337 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001338 }
Brian Salomon45c92202018-04-10 10:53:58 -04001339 }
jvanverth6ca48822016-10-07 06:57:32 -07001340
Brian Salomon05441c42017-05-15 16:45:49 -04001341 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001342 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001343
Brian Osman7d8f82b2018-11-08 10:24:09 -05001344 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001345 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1346 color,
1347 kOctagonInner[i] * innerRadius,
1348 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001349 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001350 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001351 }
1352 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001353 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001354 }
1355 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001356 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001357 }
1358 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001359 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001360 }
Brian Salomon45c92202018-04-10 10:53:58 -04001361 }
jvanverth6ca48822016-10-07 06:57:32 -07001362 } else {
1363 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001365 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001366 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001367 }
jvanverth6ca48822016-10-07 06:57:32 -07001368 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001369 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001370 }
1371 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001372 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001373 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001374 if (fRoundCaps) {
1375 vertices.write(circle.fRoundCapCenters);
1376 }
jvanverth6ca48822016-10-07 06:57:32 -07001377 }
1378
Brian Salomon05441c42017-05-15 16:45:49 -04001379 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1380 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001381 for (int i = 0; i < primIndexCount; ++i) {
1382 *indices++ = primIndices[i] + currStartVertex;
1383 }
1384
Brian Salomon05441c42017-05-15 16:45:49 -04001385 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001386 }
jvanverth6ca48822016-10-07 06:57:32 -07001387
Robert Phillips4490d922020-03-03 14:50:59 -05001388 fMesh = target->allocMesh();
1389 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001390 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001391 }
1392
1393 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001394 if (!fProgramInfo || !fMesh) {
1395 return;
1396 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001397
Chris Dalton765ed362020-03-16 17:34:44 -06001398 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001399 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001400 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001401 }
1402
Herb Derbye25c3002020-10-27 15:57:27 -04001403 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001404 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001405
1406 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001407 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001408 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001409 }
1410
Brian Salomon05441c42017-05-15 16:45:49 -04001411 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001412 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001413 }
1414
Brian Salomon05441c42017-05-15 16:45:49 -04001415 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001416 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1417 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001418 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001419 }
1420
Brian Salomon289e3d82016-12-14 15:52:56 -05001421 // Because we've set up the ops that don't use the planes with noop values
1422 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001423 fClipPlane |= that->fClipPlane;
1424 fClipPlaneIsect |= that->fClipPlaneIsect;
1425 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001426 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001427 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001428
Brian Salomon05441c42017-05-15 16:45:49 -04001429 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001430 fVertCount += that->fVertCount;
1431 fIndexCount += that->fIndexCount;
1432 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001433 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001434 }
1435
John Stilesaf366522020-08-13 09:57:34 -04001436#if GR_TEST_UTILS
1437 SkString onDumpInfo() const override {
1438 SkString string;
1439 for (int i = 0; i < fCircles.count(); ++i) {
1440 string.appendf(
1441 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1442 "InnerRad: %.2f, OuterRad: %.2f\n",
1443 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1444 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1445 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1446 fCircles[i].fOuterRadius);
1447 }
1448 string += fHelper.dumpInfo();
1449 return string;
1450 }
1451#endif
1452
Brian Salomon05441c42017-05-15 16:45:49 -04001453 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001454 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 SkScalar fInnerRadius;
1456 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001457 SkScalar fClipPlane[3];
1458 SkScalar fIsectPlane[3];
1459 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001460 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 SkRect fDevBounds;
1462 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001463 };
1464
Brian Salomon289e3d82016-12-14 15:52:56 -05001465 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001466 Helper fHelper;
1467 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001468 int fVertCount;
1469 int fIndexCount;
1470 bool fAllFill;
1471 bool fClipPlane;
1472 bool fClipPlaneIsect;
1473 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001474 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001475 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001476
Chris Daltoneb694b72020-03-16 09:25:50 -06001477 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001478 GrProgramInfo* fProgramInfo = nullptr;
1479
John Stiles7571f9e2020-09-02 22:42:33 -04001480 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08001481};
1482
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001483class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1484private:
1485 using Helper = GrSimpleMeshDrawOpHelper;
1486
1487public:
1488 DEFINE_OP_CLASS_ID
1489
Herb Derbyc76d4092020-10-07 16:46:15 -04001490 static GrOp::Owner Make(GrRecordingContext* context,
1491 GrPaint&& paint,
1492 const SkMatrix& viewMatrix,
1493 SkPoint center,
1494 SkScalar radius,
1495 SkScalar strokeWidth,
1496 SkScalar startAngle,
1497 SkScalar onAngle,
1498 SkScalar offAngle,
1499 SkScalar phaseAngle) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001500 SkASSERT(circle_stays_circle(viewMatrix));
1501 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001502 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1503 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001504 onAngle, offAngle, phaseAngle);
1505 }
1506
Herb Derbyc76d4092020-10-07 16:46:15 -04001507 ButtCapDashedCircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001508 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1509 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1510 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001511 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001512 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001513 SkASSERT(circle_stays_circle(viewMatrix));
1514 viewMatrix.mapPoints(&center, 1);
1515 radius = viewMatrix.mapRadius(radius);
1516 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1517
1518 // Determine the angle where the circle starts in device space and whether its orientation
1519 // has been reversed.
1520 SkVector start;
1521 bool reflection;
1522 if (!startAngle) {
1523 start = {1, 0};
1524 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001525 start.fY = SkScalarSin(startAngle);
1526 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001527 }
1528 viewMatrix.mapVectors(&start, 1);
1529 startAngle = SkScalarATan2(start.fY, start.fX);
1530 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1531 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1532
1533 auto totalAngle = onAngle + offAngle;
1534 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1535
1536 SkScalar halfWidth = 0;
1537 if (SkScalarNearlyZero(strokeWidth)) {
1538 halfWidth = SK_ScalarHalf;
1539 } else {
1540 halfWidth = SkScalarHalf(strokeWidth);
1541 }
1542
1543 SkScalar outerRadius = radius + halfWidth;
1544 SkScalar innerRadius = radius - halfWidth;
1545
1546 // The radii are outset for two reasons. First, it allows the shader to simply perform
1547 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1548 // Second, the outer radius is used to compute the verts of the bounding box that is
1549 // rendered and the outset ensures the box will cover all partially covered by the circle.
1550 outerRadius += SK_ScalarHalf;
1551 innerRadius -= SK_ScalarHalf;
1552 fViewMatrixIfUsingLocalCoords = viewMatrix;
1553
1554 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1555 center.fX + outerRadius, center.fY + outerRadius);
1556
1557 // We store whether there is a reflection as a negative total angle.
1558 if (reflection) {
1559 totalAngle = -totalAngle;
1560 }
1561 fCircles.push_back(Circle{
1562 color,
1563 outerRadius,
1564 innerRadius,
1565 onAngle,
1566 totalAngle,
1567 startAngle,
1568 phaseAngle,
1569 devBounds
1570 });
1571 // Use the original radius and stroke radius for the bounds so that it does not include the
1572 // AA bloat.
1573 radius += halfWidth;
1574 this->setBounds(
1575 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001576 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001577 fVertCount = circle_type_to_vert_count(true);
1578 fIndexCount = circle_type_to_index_count(true);
1579 }
1580
1581 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1582
Chris Dalton1706cbf2019-05-21 19:35:29 -06001583 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001584 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001585 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001586 } else {
1587 fHelper.visitProxies(func);
1588 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001589 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001590
Chris Dalton6ce447a2019-06-23 18:07:38 -06001591 GrProcessorSet::Analysis finalize(
1592 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1593 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001594 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001595 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001596 GrProcessorAnalysisCoverage::kSingleChannel, color,
1597 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001598 }
1599
1600 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1601
1602private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001603 GrProgramInfo* programInfo() override { return fProgramInfo; }
1604
Robert Phillips4133dc42020-03-11 15:55:55 -04001605 void onCreateProgramInfo(const GrCaps* caps,
1606 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001607 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001608 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001609 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001610 GrXferBarrierFlags renderPassXferBarriers,
1611 GrLoadOp colorLoadOp) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001612 SkMatrix localMatrix;
1613 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001614 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001615 }
1616
1617 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001618 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001619 fWideColor,
1620 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001621
Brian Salomon8afde5f2020-04-01 16:22:00 -04001622 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001623 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001624 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001625 }
1626
Robert Phillips4490d922020-03-03 14:50:59 -05001627 void onPrepareDraws(Target* target) override {
1628 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001629 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001630 if (!fProgramInfo) {
1631 return;
1632 }
1633 }
1634
Brian Salomon12d22642019-01-29 14:38:50 -05001635 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001636 int firstVertex;
Robert Phillips787fd9d2021-03-22 14:48:09 -04001637 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05001638 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001639 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001640 SkDebugf("Could not allocate vertices\n");
1641 return;
1642 }
1643
Brian Salomon12d22642019-01-29 14:38:50 -05001644 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001645 int firstIndex = 0;
1646 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1647 if (!indices) {
1648 SkDebugf("Could not allocate indices\n");
1649 return;
1650 }
1651
1652 int currStartVertex = 0;
1653 for (const auto& circle : fCircles) {
1654 // The inner radius in the vertex data must be specified in normalized space so that
1655 // length() can be called with smaller values to avoid precision issues with half
1656 // floats.
1657 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1658 const SkRect& bounds = circle.fDevBounds;
1659 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001660 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1661 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1662 };
1663 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001664 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001665 dashParams.totalAngle = -dashParams.totalAngle;
1666 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 }
1668
Brian Osmane3caf2d2018-11-21 13:48:36 -05001669 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001670
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001671 // The bounding geometry for the circle is composed of an outer bounding octagon and
1672 // an inner bounded octagon.
1673
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001674 // Compute the vertices of the outer octagon.
1675 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1676 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001677
1678 auto reflectY = [=](const SkPoint& p) {
1679 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001680 };
Brian Osman9d958b52018-11-13 12:46:56 -05001681
1682 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001683 vertices.write(center + kOctagonOuter[i] * halfWidth,
1684 color,
1685 reflectY(kOctagonOuter[i]),
1686 circle.fOuterRadius,
1687 normInnerRadius,
1688 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001689 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001690
1691 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001692 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001693 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1694 color,
1695 reflectY(kOctagonInner[i]) * normInnerRadius,
1696 circle.fOuterRadius,
1697 normInnerRadius,
1698 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001699 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001700
1701 const uint16_t* primIndices = circle_type_to_indices(true);
1702 const int primIndexCount = circle_type_to_index_count(true);
1703 for (int i = 0; i < primIndexCount; ++i) {
1704 *indices++ = primIndices[i] + currStartVertex;
1705 }
1706
1707 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001708 }
1709
Robert Phillips4490d922020-03-03 14:50:59 -05001710 fMesh = target->allocMesh();
1711 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001712 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001713 }
1714
1715 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001716 if (!fProgramInfo || !fMesh) {
1717 return;
1718 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001719
Chris Dalton765ed362020-03-16 17:34:44 -06001720 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04001721 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06001722 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 }
1724
Herb Derbye25c3002020-10-27 15:57:27 -04001725 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001726 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1727
1728 // can only represent 65535 unique vertices with 16-bit indices
1729 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001730 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001731 }
1732
1733 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001734 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001735 }
1736
1737 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001738 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1739 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001740 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 }
1742
1743 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001744 fVertCount += that->fVertCount;
1745 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001746 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001747 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001748 }
1749
John Stilesaf366522020-08-13 09:57:34 -04001750#if GR_TEST_UTILS
1751 SkString onDumpInfo() const override {
1752 SkString string;
1753 for (int i = 0; i < fCircles.count(); ++i) {
1754 string.appendf(
1755 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1756 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1757 "Phase: %.2f\n",
1758 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1759 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1760 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1761 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1762 fCircles[i].fPhaseAngle);
1763 }
1764 string += fHelper.dumpInfo();
1765 return string;
1766 }
1767#endif
1768
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001769 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001770 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001771 SkScalar fOuterRadius;
1772 SkScalar fInnerRadius;
1773 SkScalar fOnAngle;
1774 SkScalar fTotalAngle;
1775 SkScalar fStartAngle;
1776 SkScalar fPhaseAngle;
1777 SkRect fDevBounds;
1778 };
1779
1780 SkMatrix fViewMatrixIfUsingLocalCoords;
1781 Helper fHelper;
1782 SkSTArray<1, Circle, true> fCircles;
1783 int fVertCount;
1784 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001785 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001786
Chris Daltoneb694b72020-03-16 09:25:50 -06001787 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001788 GrProgramInfo* fProgramInfo = nullptr;
1789
John Stiles7571f9e2020-09-02 22:42:33 -04001790 using INHERITED = GrMeshDrawOp;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001791};
1792
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001793///////////////////////////////////////////////////////////////////////////////
1794
Brian Salomon05441c42017-05-15 16:45:49 -04001795class EllipseOp : public GrMeshDrawOp {
1796private:
1797 using Helper = GrSimpleMeshDrawOpHelper;
1798
1799 struct DeviceSpaceParams {
1800 SkPoint fCenter;
1801 SkScalar fXRadius;
1802 SkScalar fYRadius;
1803 SkScalar fInnerXRadius;
1804 SkScalar fInnerYRadius;
1805 };
1806
joshualitt76e7fb62015-02-11 08:52:27 -08001807public:
Brian Salomon25a88092016-12-01 09:36:50 -05001808 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001809
Herb Derbyc76d4092020-10-07 16:46:15 -04001810 static GrOp::Owner Make(GrRecordingContext* context,
1811 GrPaint&& paint,
1812 const SkMatrix& viewMatrix,
1813 const SkRect& ellipse,
1814 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001815 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001816 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001817 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1818 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001819 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1820 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001821 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1822 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1823 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1824 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001825
bsalomon4b4a7cc2016-07-08 04:42:54 -07001826 // do (potentially) anisotropic mapping of stroke
1827 SkVector scaledStroke;
1828 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001829 scaledStroke.fX = SkScalarAbs(
1830 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1831 scaledStroke.fY = SkScalarAbs(
1832 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001833
1834 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001835 bool isStrokeOnly =
1836 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001837 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1838
Brian Salomon05441c42017-05-15 16:45:49 -04001839 params.fInnerXRadius = 0;
1840 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001841 if (hasStroke) {
1842 if (SkScalarNearlyZero(scaledStroke.length())) {
1843 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1844 } else {
1845 scaledStroke.scale(SK_ScalarHalf);
1846 }
1847
1848 // we only handle thick strokes for near-circular ellipses
1849 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001850 (0.5f * params.fXRadius > params.fYRadius ||
1851 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852 return nullptr;
1853 }
1854
1855 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001856 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1857 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1858 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1859 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001860 return nullptr;
1861 }
1862
1863 // this is legit only if scale & translation (which should be the case at the moment)
1864 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001865 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1866 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001867 }
1868
Brian Salomon05441c42017-05-15 16:45:49 -04001869 params.fXRadius += scaledStroke.fX;
1870 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001871 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001872
1873 // For large ovals with low precision floats, we fall back to the path renderer.
1874 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1875 // minimum value to avoid divides by zero. With large ovals and low precision this
1876 // leads to blurring at the edge of the oval.
1877 const SkScalar kMaxOvalRadius = 16384;
1878 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1879 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1880 return nullptr;
1881 }
1882
Greg Daniel2655ede2019-04-10 00:49:28 +00001883 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001884 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001885 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001886
Herb Derbyc76d4092020-10-07 16:46:15 -04001887 EllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001888 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001889 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001890 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001891 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001892 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001893 SkStrokeRec::Style style = stroke.getStyle();
1894 bool isStrokeOnly =
1895 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001896
Brian Salomon05441c42017-05-15 16:45:49 -04001897 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1898 params.fInnerXRadius, params.fInnerYRadius,
1899 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1900 params.fCenter.fY - params.fYRadius,
1901 params.fCenter.fX + params.fXRadius,
1902 params.fCenter.fY + params.fYRadius)});
1903
Greg Daniel5faf4742019-10-01 15:14:44 -04001904 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001905
bsalomon4b4a7cc2016-07-08 04:42:54 -07001906 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001907 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001908
Brian Salomon05441c42017-05-15 16:45:49 -04001909 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1910 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001911 }
joshualitt76e7fb62015-02-11 08:52:27 -08001912
Brian Salomon289e3d82016-12-14 15:52:56 -05001913 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001914
Chris Dalton1706cbf2019-05-21 19:35:29 -06001915 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001916 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001917 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001918 } else {
1919 fHelper.visitProxies(func);
1920 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001921 }
1922
Chris Dalton6ce447a2019-06-23 18:07:38 -06001923 GrProcessorSet::Analysis finalize(
1924 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1925 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001926 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1927 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001928 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001929 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001930 GrProcessorAnalysisCoverage::kSingleChannel, color,
1931 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001932 }
1933
1934 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1935
bsalomone46f9fe2015-08-18 06:05:14 -07001936private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001937 GrProgramInfo* programInfo() override { return fProgramInfo; }
1938
Robert Phillips4133dc42020-03-11 15:55:55 -04001939 void onCreateProgramInfo(const GrCaps* caps,
1940 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05001941 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001942 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001943 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05001944 GrXferBarrierFlags renderPassXferBarriers,
1945 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001946 SkMatrix localMatrix;
1947 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001948 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001949 }
1950
Robert Phillips4490d922020-03-03 14:50:59 -05001951 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1952 fUseScale, localMatrix);
1953
Brian Salomon8afde5f2020-04-01 16:22:00 -04001954 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001955 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05001956 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05001957 }
1958
Robert Phillips4490d922020-03-03 14:50:59 -05001959 void onPrepareDraws(Target* target) override {
1960 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001961 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001962 if (!fProgramInfo) {
1963 return;
1964 }
1965 }
1966
Robert Phillips787fd9d2021-03-22 14:48:09 -04001967 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001968 GrVertexWriter verts{helper.vertices()};
1969 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001970 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001971 return;
1972 }
1973
Brian Salomon05441c42017-05-15 16:45:49 -04001974 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001975 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001976 SkScalar xRadius = ellipse.fXRadius;
1977 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001978
1979 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001980 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1981 SkScalarInvert(xRadius),
1982 SkScalarInvert(yRadius),
1983 SkScalarInvert(ellipse.fInnerXRadius),
1984 SkScalarInvert(ellipse.fInnerYRadius)
1985 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001986 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1987 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001988
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001989 if (!fStroked) {
1990 // For filled ellipses we map a unit circle in the vertex attributes rather than
1991 // computing an ellipse and modifying that distance, so we normalize to 1
1992 xMaxOffset /= xRadius;
1993 yMaxOffset /= yRadius;
1994 }
1995
joshualitt76e7fb62015-02-11 08:52:27 -08001996 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001997 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1998 color,
1999 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002000 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002001 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002002 }
Robert Phillips4490d922020-03-03 14:50:59 -05002003 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002004 }
2005
2006 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002007 if (!fProgramInfo || !fMesh) {
2008 return;
2009 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002010
Chris Dalton765ed362020-03-16 17:34:44 -06002011 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002012 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002013 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002014 }
2015
Herb Derbye25c3002020-10-27 15:57:27 -04002016 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002017 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002018
Brian Salomon05441c42017-05-15 16:45:49 -04002019 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002020 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002021 }
2022
bsalomoncdaa97b2016-03-08 08:30:14 -08002023 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002024 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002025 }
2026
Brian Salomon05441c42017-05-15 16:45:49 -04002027 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002028 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2029 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002030 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002031 }
2032
Brian Salomon05441c42017-05-15 16:45:49 -04002033 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002034 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002035 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002036 }
2037
John Stilesaf366522020-08-13 09:57:34 -04002038#if GR_TEST_UTILS
2039 SkString onDumpInfo() const override {
2040 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2041 for (const auto& geo : fEllipses) {
2042 string.appendf(
2043 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2044 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2045 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2046 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2047 geo.fInnerXRadius, geo.fInnerYRadius);
2048 }
2049 string += fHelper.dumpInfo();
2050 return string;
2051 }
2052#endif
2053
Brian Salomon05441c42017-05-15 16:45:49 -04002054 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002055 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002056 SkScalar fXRadius;
2057 SkScalar fYRadius;
2058 SkScalar fInnerXRadius;
2059 SkScalar fInnerYRadius;
2060 SkRect fDevBounds;
2061 };
joshualitt76e7fb62015-02-11 08:52:27 -08002062
Brian Salomon289e3d82016-12-14 15:52:56 -05002063 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002064 Helper fHelper;
2065 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002066 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002067 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002068 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002069
Chris Daltoneb694b72020-03-16 09:25:50 -06002070 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002071 GrProgramInfo* fProgramInfo = nullptr;
2072
John Stiles7571f9e2020-09-02 22:42:33 -04002073 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002074};
2075
joshualitt76e7fb62015-02-11 08:52:27 -08002076/////////////////////////////////////////////////////////////////////////////////////////////////
2077
Brian Salomon05441c42017-05-15 16:45:49 -04002078class DIEllipseOp : public GrMeshDrawOp {
2079private:
2080 using Helper = GrSimpleMeshDrawOpHelper;
2081
2082 struct DeviceSpaceParams {
2083 SkPoint fCenter;
2084 SkScalar fXRadius;
2085 SkScalar fYRadius;
2086 SkScalar fInnerXRadius;
2087 SkScalar fInnerYRadius;
2088 DIEllipseStyle fStyle;
2089 };
2090
joshualitt76e7fb62015-02-11 08:52:27 -08002091public:
Brian Salomon25a88092016-12-01 09:36:50 -05002092 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002093
Herb Derbyc76d4092020-10-07 16:46:15 -04002094 static GrOp::Owner Make(GrRecordingContext* context,
2095 GrPaint&& paint,
2096 const SkMatrix& viewMatrix,
2097 const SkRect& ellipse,
2098 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002099 DeviceSpaceParams params;
2100 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2101 params.fXRadius = SkScalarHalf(ellipse.width());
2102 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002103
bsalomon4b4a7cc2016-07-08 04:42:54 -07002104 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002105 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2106 ? DIEllipseStyle::kStroke
2107 : (SkStrokeRec::kHairline_Style == style)
2108 ? DIEllipseStyle::kHairline
2109 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002110
Brian Salomon05441c42017-05-15 16:45:49 -04002111 params.fInnerXRadius = 0;
2112 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002113 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2114 SkScalar strokeWidth = stroke.getWidth();
2115
2116 if (SkScalarNearlyZero(strokeWidth)) {
2117 strokeWidth = SK_ScalarHalf;
2118 } else {
2119 strokeWidth *= SK_ScalarHalf;
2120 }
2121
2122 // we only handle thick strokes for near-circular ellipses
2123 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002124 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2125 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002126 return nullptr;
2127 }
2128
2129 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002130 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2131 (strokeWidth * strokeWidth) * params.fXRadius) {
2132 return nullptr;
2133 }
2134 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2135 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002136 return nullptr;
2137 }
2138
2139 // set inner radius (if needed)
2140 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002141 params.fInnerXRadius = params.fXRadius - strokeWidth;
2142 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002143 }
2144
Brian Salomon05441c42017-05-15 16:45:49 -04002145 params.fXRadius += strokeWidth;
2146 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002147 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002148
2149 // For large ovals with low precision floats, we fall back to the path renderer.
2150 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2151 // minimum value to avoid divides by zero. With large ovals and low precision this
2152 // leads to blurring at the edge of the oval.
2153 const SkScalar kMaxOvalRadius = 16384;
2154 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2155 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2156 return nullptr;
2157 }
2158
Brian Salomon05441c42017-05-15 16:45:49 -04002159 if (DIEllipseStyle::kStroke == params.fStyle &&
2160 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2161 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002162 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002163 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002164 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002165
Herb Derbyc76d4092020-10-07 16:46:15 -04002166 DIEllipseOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002167 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002168 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002169 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002170 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002171 // This expands the outer rect so that after CTM we end up with a half-pixel border
2172 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2173 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2174 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2175 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2176 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2177 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002178
Brian Salomon05441c42017-05-15 16:45:49 -04002179 fEllipses.emplace_back(
2180 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2181 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2182 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2183 params.fCenter.fY - params.fYRadius - geoDy,
2184 params.fCenter.fX + params.fXRadius + geoDx,
2185 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002186 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002187 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002188 }
2189
Brian Salomon289e3d82016-12-14 15:52:56 -05002190 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002191
Chris Dalton1706cbf2019-05-21 19:35:29 -06002192 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002193 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002194 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002195 } else {
2196 fHelper.visitProxies(func);
2197 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002198 }
2199
Chris Dalton6ce447a2019-06-23 18:07:38 -06002200 GrProcessorSet::Analysis finalize(
2201 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2202 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002203 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2204 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002205 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002206 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002207 GrProcessorAnalysisCoverage::kSingleChannel, color,
2208 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002209 }
2210
2211 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2212
bsalomone46f9fe2015-08-18 06:05:14 -07002213private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002214 GrProgramInfo* programInfo() override { return fProgramInfo; }
2215
Robert Phillips4133dc42020-03-11 15:55:55 -04002216 void onCreateProgramInfo(const GrCaps* caps,
2217 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002218 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002219 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002220 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002221 GrXferBarrierFlags renderPassXferBarriers,
2222 GrLoadOp colorLoadOp) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002223 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2224 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002225 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002226
Brian Salomon8afde5f2020-04-01 16:22:00 -04002227 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002228 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002229 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002230 }
2231
Robert Phillips4490d922020-03-03 14:50:59 -05002232 void onPrepareDraws(Target* target) override {
2233 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002234 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002235 }
2236
Robert Phillips787fd9d2021-03-22 14:48:09 -04002237 QuadHelper helper(target, fProgramInfo->geomProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002238 GrVertexWriter verts{helper.vertices()};
2239 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002240 return;
2241 }
2242
Brian Salomon05441c42017-05-15 16:45:49 -04002243 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002244 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002245 SkScalar xRadius = ellipse.fXRadius;
2246 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002247
joshualitt76e7fb62015-02-11 08:52:27 -08002248 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002249 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2250 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002251
Brian Osman9d958b52018-11-13 12:46:56 -05002252 // By default, constructed so that inner offset is (0, 0) for all points
2253 SkScalar innerRatioX = -offsetDx;
2254 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002255
Brian Osman9d958b52018-11-13 12:46:56 -05002256 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002257 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002258 innerRatioX = xRadius / ellipse.fInnerXRadius;
2259 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002260 }
joshualitt76e7fb62015-02-11 08:52:27 -08002261
Brian Osman2b6e3902018-11-21 15:29:43 -05002262 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2263 color,
2264 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002265 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002266 origin_centered_tri_strip(innerRatioX + offsetDx,
2267 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002268 }
Robert Phillips4490d922020-03-03 14:50:59 -05002269 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002270 }
2271
2272 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002273 if (!fProgramInfo || !fMesh) {
2274 return;
2275 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002276
Chris Dalton765ed362020-03-16 17:34:44 -06002277 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002278 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002279 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002280 }
halcanary9d524f22016-03-29 09:03:52 -07002281
Herb Derbye25c3002020-10-27 15:57:27 -04002282 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002283 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002284 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002285 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002286 }
2287
bsalomoncdaa97b2016-03-08 08:30:14 -08002288 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002289 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002290 }
2291
joshualittd96a67b2015-05-05 14:09:05 -07002292 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002293 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002294 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002295 }
2296
Brian Salomon05441c42017-05-15 16:45:49 -04002297 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002298 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002299 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002300 }
2301
John Stilesaf366522020-08-13 09:57:34 -04002302#if GR_TEST_UTILS
2303 SkString onDumpInfo() const override {
2304 SkString string;
2305 for (const auto& geo : fEllipses) {
2306 string.appendf(
2307 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2308 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2309 "GeoDY: %.2f\n",
2310 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2311 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2312 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2313 }
2314 string += fHelper.dumpInfo();
2315 return string;
2316 }
2317#endif
2318
Brian Salomon05441c42017-05-15 16:45:49 -04002319 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2320 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002321
Brian Salomon05441c42017-05-15 16:45:49 -04002322 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002323 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002324 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002325 SkScalar fXRadius;
2326 SkScalar fYRadius;
2327 SkScalar fInnerXRadius;
2328 SkScalar fInnerYRadius;
2329 SkScalar fGeoDx;
2330 SkScalar fGeoDy;
2331 DIEllipseStyle fStyle;
2332 SkRect fBounds;
2333 };
2334
Brian Salomon05441c42017-05-15 16:45:49 -04002335 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002336 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002337 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002338 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002339
Chris Daltoneb694b72020-03-16 09:25:50 -06002340 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002341 GrProgramInfo* fProgramInfo = nullptr;
2342
John Stiles7571f9e2020-09-02 22:42:33 -04002343 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002344};
2345
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002346///////////////////////////////////////////////////////////////////////////////
2347
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002348// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002349//
2350// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2351// ____________
2352// |_|________|_|
2353// | | | |
2354// | | | |
2355// | | | |
2356// |_|________|_|
2357// |_|________|_|
2358//
2359// For strokes, we don't draw the center quad.
2360//
2361// For circular roundrects, in the case where the stroke width is greater than twice
2362// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002363// in the center. The shared vertices are duplicated so we can set a different outer radius
2364// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002365// ____________
2366// |_|________|_|
2367// | |\ ____ /| |
2368// | | | | | |
2369// | | |____| | |
2370// |_|/______\|_|
2371// |_|________|_|
2372//
2373// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002374//
2375// For filled rrects that need to provide a distance vector we resuse the overstroke
2376// geometry but make the inner rect degenerate (either a point or a horizontal or
2377// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002378
jvanverth84839f62016-08-29 10:16:40 -07002379static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002380 // clang-format off
2381 // overstroke quads
2382 // we place this at the beginning so that we can skip these indices when rendering normally
2383 16, 17, 19, 16, 19, 18,
2384 19, 17, 23, 19, 23, 21,
2385 21, 23, 22, 21, 22, 20,
2386 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002387
Brian Salomon289e3d82016-12-14 15:52:56 -05002388 // corners
2389 0, 1, 5, 0, 5, 4,
2390 2, 3, 7, 2, 7, 6,
2391 8, 9, 13, 8, 13, 12,
2392 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002393
Brian Salomon289e3d82016-12-14 15:52:56 -05002394 // edges
2395 1, 2, 6, 1, 6, 5,
2396 4, 5, 9, 4, 9, 8,
2397 6, 7, 11, 6, 11, 10,
2398 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002399
Brian Salomon289e3d82016-12-14 15:52:56 -05002400 // center
2401 // we place this at the end so that we can ignore these indices when not rendering as filled
2402 5, 6, 10, 5, 10, 9,
2403 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002404};
Brian Salomon289e3d82016-12-14 15:52:56 -05002405
jvanverth84839f62016-08-29 10:16:40 -07002406// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002407static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002408
jvanverth84839f62016-08-29 10:16:40 -07002409// overstroke count is arraysize minus the center indices
2410static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2411// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002412static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002413// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002414static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2415static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002416static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002417
jvanverthc3d0e422016-08-25 08:12:35 -07002418enum RRectType {
2419 kFill_RRectType,
2420 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002421 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002422};
2423
jvanverth84839f62016-08-29 10:16:40 -07002424static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002425 switch (type) {
2426 case kFill_RRectType:
2427 case kStroke_RRectType:
2428 return kVertsPerStandardRRect;
2429 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002430 return kVertsPerOverstrokeRRect;
2431 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002432 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002433}
2434
2435static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002436 switch (type) {
2437 case kFill_RRectType:
2438 return kIndicesPerFillRRect;
2439 case kStroke_RRectType:
2440 return kIndicesPerStrokeRRect;
2441 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002442 return kIndicesPerOverstrokeRRect;
2443 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002444 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002445}
2446
2447static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002448 switch (type) {
2449 case kFill_RRectType:
2450 case kStroke_RRectType:
2451 return gStandardRRectIndices;
2452 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002453 return gOverstrokeRRectIndices;
2454 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002455 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002456}
2457
joshualitt76e7fb62015-02-11 08:52:27 -08002458///////////////////////////////////////////////////////////////////////////////////////////////////
2459
Robert Phillips79839d42016-10-06 15:03:34 -04002460// For distance computations in the interior of filled rrects we:
2461//
2462// add a interior degenerate (point or line) rect
2463// each vertex of that rect gets -outerRad as its radius
2464// this makes the computation of the distance to the outer edge be negative
2465// negative values are caught and then handled differently in the GP's onEmitCode
2466// each vertex is also given the normalized x & y distance from the interior rect's edge
2467// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2468
Brian Salomon05441c42017-05-15 16:45:49 -04002469class CircularRRectOp : public GrMeshDrawOp {
2470private:
2471 using Helper = GrSimpleMeshDrawOpHelper;
2472
joshualitt76e7fb62015-02-11 08:52:27 -08002473public:
Brian Salomon25a88092016-12-01 09:36:50 -05002474 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002475
bsalomon4b4a7cc2016-07-08 04:42:54 -07002476 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2477 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002478 static GrOp::Owner Make(GrRecordingContext* context,
2479 GrPaint&& paint,
2480 const SkMatrix& viewMatrix,
2481 const SkRect& devRect,
2482 float devRadius,
2483 float devStrokeWidth,
2484 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002485 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002486 devRect, devRadius,
2487 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002488 }
Herb Derbyc76d4092020-10-07 16:46:15 -04002489 CircularRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002490 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2491 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002492 : INHERITED(ClassID())
2493 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Herb Derbyc76d4092020-10-07 16:46:15 -04002494 , fHelper(processorSet, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002495 SkRect bounds = devRect;
2496 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2497 SkScalar innerRadius = 0.0f;
2498 SkScalar outerRadius = devRadius;
2499 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002500 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002501 if (devStrokeWidth > 0) {
2502 if (SkScalarNearlyZero(devStrokeWidth)) {
2503 halfWidth = SK_ScalarHalf;
2504 } else {
2505 halfWidth = SkScalarHalf(devStrokeWidth);
2506 }
joshualitt76e7fb62015-02-11 08:52:27 -08002507
bsalomon4b4a7cc2016-07-08 04:42:54 -07002508 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002509 // Outset stroke by 1/4 pixel
2510 devStrokeWidth += 0.25f;
2511 // If stroke is greater than width or height, this is still a fill
2512 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002513 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002514 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002515 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002516 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002517 }
2518 outerRadius += halfWidth;
2519 bounds.outset(halfWidth, halfWidth);
2520 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002521
Greg Daniel2655ede2019-04-10 00:49:28 +00002522 // The radii are outset for two reasons. First, it allows the shader to simply perform
2523 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2524 // Second, the outer radius is used to compute the verts of the bounding box that is
2525 // rendered and the outset ensures the box will cover all partially covered by the rrect
2526 // corners.
2527 outerRadius += SK_ScalarHalf;
2528 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002529
Greg Daniel5faf4742019-10-01 15:14:44 -04002530 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002531
Greg Daniel2655ede2019-04-10 00:49:28 +00002532 // Expand the rect for aa to generate correct vertices.
2533 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002534
Brian Salomon05441c42017-05-15 16:45:49 -04002535 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002536 fVertCount = rrect_type_to_vert_count(type);
2537 fIndexCount = rrect_type_to_index_count(type);
2538 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002539 }
2540
Brian Salomon289e3d82016-12-14 15:52:56 -05002541 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002542
Chris Dalton1706cbf2019-05-21 19:35:29 -06002543 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002544 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002545 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002546 } else {
2547 fHelper.visitProxies(func);
2548 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002549 }
2550
Chris Dalton6ce447a2019-06-23 18:07:38 -06002551 GrProcessorSet::Analysis finalize(
2552 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2553 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002554 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002555 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002556 GrProcessorAnalysisCoverage::kSingleChannel, color,
2557 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002558 }
2559
2560 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2561
Brian Salomon92aee3d2016-12-21 09:20:25 -05002562private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002563 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002564 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002565 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002566 SkASSERT(smInset < bigInset);
2567
2568 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002569 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2570 color,
2571 xOffset, 0.0f,
2572 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002573
2574 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002575 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2576 color,
2577 xOffset, 0.0f,
2578 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002579
Brian Osmana1d4eb92018-12-06 16:33:10 -05002580 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2581 color,
2582 0.0f, 0.0f,
2583 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002584
Brian Osmana1d4eb92018-12-06 16:33:10 -05002585 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2586 color,
2587 0.0f, 0.0f,
2588 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002589
Brian Osmana1d4eb92018-12-06 16:33:10 -05002590 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2591 color,
2592 0.0f, 0.0f,
2593 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002594
Brian Osmana1d4eb92018-12-06 16:33:10 -05002595 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2596 color,
2597 0.0f, 0.0f,
2598 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002599
2600 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002601 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2602 color,
2603 xOffset, 0.0f,
2604 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002605
2606 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002607 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2608 color,
2609 xOffset, 0.0f,
2610 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002611 }
2612
Robert Phillips2669a7b2020-03-12 12:07:19 -04002613 GrProgramInfo* programInfo() override { return fProgramInfo; }
2614
Robert Phillips4133dc42020-03-11 15:55:55 -04002615 void onCreateProgramInfo(const GrCaps* caps,
2616 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002617 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002618 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002619 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002620 GrXferBarrierFlags renderPassXferBarriers,
2621 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002622 // Invert the view matrix as a local matrix (if any other processors require coords).
2623 SkMatrix localMatrix;
2624 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002625 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002626 }
2627
Robert Phillips4490d922020-03-03 14:50:59 -05002628 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002629 false, false, false, false,
2630 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002631
Brian Salomon8afde5f2020-04-01 16:22:00 -04002632 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002633 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002634 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002635 }
2636
Robert Phillips4490d922020-03-03 14:50:59 -05002637 void onPrepareDraws(Target* target) override {
2638 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002639 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002640 if (!fProgramInfo) {
2641 return;
2642 }
2643 }
2644
Brian Salomon12d22642019-01-29 14:38:50 -05002645 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002646 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002647
Robert Phillips787fd9d2021-03-22 14:48:09 -04002648 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->geomProc().vertexStride(),
Robert Phillips4490d922020-03-03 14:50:59 -05002649 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002650 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002651 SkDebugf("Could not allocate vertices\n");
2652 return;
2653 }
2654
Brian Salomon12d22642019-01-29 14:38:50 -05002655 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002656 int firstIndex = 0;
2657 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2658 if (!indices) {
2659 SkDebugf("Could not allocate indices\n");
2660 return;
2661 }
2662
2663 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002664 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002665 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002666 SkScalar outerRadius = rrect.fOuterRadius;
2667 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002668
Brian Salomon289e3d82016-12-14 15:52:56 -05002669 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2670 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002671
Brian Salomon289e3d82016-12-14 15:52:56 -05002672 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002673 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002674 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002675 SkScalar innerRadius = rrect.fType != kFill_RRectType
2676 ? rrect.fInnerRadius / rrect.fOuterRadius
2677 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002678 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002679 verts.write(bounds.fLeft, yCoords[i],
2680 color,
2681 -1.0f, yOuterRadii[i],
2682 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002683
Brian Osmana1d4eb92018-12-06 16:33:10 -05002684 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2685 color,
2686 0.0f, yOuterRadii[i],
2687 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002688
Brian Osmana1d4eb92018-12-06 16:33:10 -05002689 verts.write(bounds.fRight - outerRadius, yCoords[i],
2690 color,
2691 0.0f, yOuterRadii[i],
2692 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002693
Brian Osmana1d4eb92018-12-06 16:33:10 -05002694 verts.write(bounds.fRight, yCoords[i],
2695 color,
2696 1.0f, yOuterRadii[i],
2697 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002698 }
jvanverthc3d0e422016-08-25 08:12:35 -07002699 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002700 // Effectively this is an additional stroked rrect, with its
2701 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2702 // This will give us correct AA in the center and the correct
2703 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002704 //
jvanvertha4f1af82016-08-29 07:17:47 -07002705 // Also, the outer offset is a constant vector pointing to the right, which
2706 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002707 if (kOverstroke_RRectType == rrect.fType) {
2708 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002709
Brian Salomon05441c42017-05-15 16:45:49 -04002710 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002711 // this is the normalized distance from the outer rectangle of this
2712 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002713 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002714
Brian Osmana1d4eb92018-12-06 16:33:10 -05002715 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002716 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002717 }
jvanverth6a397612016-08-26 08:15:33 -07002718
Brian Salomon05441c42017-05-15 16:45:49 -04002719 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2720 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002721 for (int i = 0; i < primIndexCount; ++i) {
2722 *indices++ = primIndices[i] + currStartVertex;
2723 }
2724
Brian Salomon05441c42017-05-15 16:45:49 -04002725 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002726 }
2727
Robert Phillips4490d922020-03-03 14:50:59 -05002728 fMesh = target->allocMesh();
2729 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002730 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002731 }
2732
2733 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002734 if (!fProgramInfo || !fMesh) {
2735 return;
2736 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002737
Chris Dalton765ed362020-03-16 17:34:44 -06002738 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04002739 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06002740 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002741 }
2742
Herb Derbye25c3002020-10-27 15:57:27 -04002743 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002744 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002745
2746 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002747 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002748 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002749 }
2750
Brian Salomon05441c42017-05-15 16:45:49 -04002751 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002752 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002753 }
2754
Brian Salomon05441c42017-05-15 16:45:49 -04002755 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002756 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2757 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002758 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002759 }
2760
Brian Salomon05441c42017-05-15 16:45:49 -04002761 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002762 fVertCount += that->fVertCount;
2763 fIndexCount += that->fIndexCount;
2764 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002765 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002766 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002767 }
2768
John Stilesaf366522020-08-13 09:57:34 -04002769#if GR_TEST_UTILS
2770 SkString onDumpInfo() const override {
2771 SkString string;
2772 for (int i = 0; i < fRRects.count(); ++i) {
2773 string.appendf(
2774 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2775 "InnerRad: %.2f, OuterRad: %.2f\n",
2776 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2777 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2778 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2779 fRRects[i].fOuterRadius);
2780 }
2781 string += fHelper.dumpInfo();
2782 return string;
2783 }
2784#endif
2785
Brian Salomon05441c42017-05-15 16:45:49 -04002786 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002787 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002788 SkScalar fInnerRadius;
2789 SkScalar fOuterRadius;
2790 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002791 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002792 };
2793
Brian Salomon289e3d82016-12-14 15:52:56 -05002794 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002795 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002796 int fVertCount;
2797 int fIndexCount;
2798 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002799 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002800 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002801
Chris Daltoneb694b72020-03-16 09:25:50 -06002802 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002803 GrProgramInfo* fProgramInfo = nullptr;
2804
John Stiles7571f9e2020-09-02 22:42:33 -04002805 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08002806};
2807
jvanverth84839f62016-08-29 10:16:40 -07002808static const int kNumRRectsInIndexBuffer = 256;
2809
2810GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2811GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002812static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2813 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002814 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2815 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2816 switch (type) {
2817 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002818 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002819 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2820 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002821 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002822 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002823 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2824 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002825 default:
2826 SkASSERT(false);
2827 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002828 }
jvanverth84839f62016-08-29 10:16:40 -07002829}
2830
Brian Salomon05441c42017-05-15 16:45:49 -04002831class EllipticalRRectOp : public GrMeshDrawOp {
2832private:
2833 using Helper = GrSimpleMeshDrawOpHelper;
2834
joshualitt76e7fb62015-02-11 08:52:27 -08002835public:
Brian Salomon25a88092016-12-01 09:36:50 -05002836 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002837
bsalomon4b4a7cc2016-07-08 04:42:54 -07002838 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2839 // whether the rrect is only stroked or stroked and filled.
Herb Derbyc76d4092020-10-07 16:46:15 -04002840 static GrOp::Owner Make(GrRecordingContext* context,
2841 GrPaint&& paint,
2842 const SkMatrix& viewMatrix,
2843 const SkRect& devRect,
2844 float devXRadius,
2845 float devYRadius,
2846 SkVector devStrokeWidths,
2847 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002848 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2849 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002850 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2851 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002852 if (devStrokeWidths.fX > 0) {
2853 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2854 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2855 } else {
2856 devStrokeWidths.scale(SK_ScalarHalf);
2857 }
joshualitt76e7fb62015-02-11 08:52:27 -08002858
bsalomon4b4a7cc2016-07-08 04:42:54 -07002859 // we only handle thick strokes for near-circular ellipses
2860 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002861 (SK_ScalarHalf * devXRadius > devYRadius ||
2862 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002863 return nullptr;
2864 }
2865
2866 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002867 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2868 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002869 return nullptr;
2870 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002871 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2872 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002873 return nullptr;
2874 }
Brian Salomon05441c42017-05-15 16:45:49 -04002875 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002876 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002877 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002878 devXRadius, devYRadius, devStrokeWidths,
2879 strokeOnly);
2880 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002881
Herb Derbyc76d4092020-10-07 16:46:15 -04002882 EllipticalRRectOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002883 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2884 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002885 : INHERITED(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04002886 , fHelper(processorSet, GrAAType::kCoverage)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002887 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002888 SkScalar innerXRadius = 0.0f;
2889 SkScalar innerYRadius = 0.0f;
2890 SkRect bounds = devRect;
2891 bool stroked = false;
2892 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002893 // this is legit only if scale & translation (which should be the case at the moment)
2894 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002895 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2896 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002897 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2898 }
2899
Brian Salomon05441c42017-05-15 16:45:49 -04002900 devXRadius += devStrokeHalfWidths.fX;
2901 devYRadius += devStrokeHalfWidths.fY;
2902 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002903 }
2904
Brian Salomon05441c42017-05-15 16:45:49 -04002905 fStroked = stroked;
2906 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002907 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002908 // Expand the rect for aa in order to generate the correct vertices.
2909 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002910 fRRects.emplace_back(
2911 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002912 }
2913
Brian Salomon289e3d82016-12-14 15:52:56 -05002914 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002915
Chris Dalton1706cbf2019-05-21 19:35:29 -06002916 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002917 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002918 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002919 } else {
2920 fHelper.visitProxies(func);
2921 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002922 }
2923
Chris Dalton6ce447a2019-06-23 18:07:38 -06002924 GrProcessorSet::Analysis finalize(
2925 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2926 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002927 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002928 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002929 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002930 GrProcessorAnalysisCoverage::kSingleChannel, color,
2931 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002932 }
2933
2934 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2935
bsalomone46f9fe2015-08-18 06:05:14 -07002936private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002937 GrProgramInfo* programInfo() override { return fProgramInfo; }
2938
Robert Phillips4133dc42020-03-11 15:55:55 -04002939 void onCreateProgramInfo(const GrCaps* caps,
2940 SkArenaAlloc* arena,
Adlai Hollere2296f72020-11-19 13:41:26 -05002941 const GrSurfaceProxyView& writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002942 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04002943 const GrXferProcessor::DstProxyView& dstProxyView,
Greg Daniel42dbca52020-11-20 10:22:43 -05002944 GrXferBarrierFlags renderPassXferBarriers,
2945 GrLoadOp colorLoadOp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002946 SkMatrix localMatrix;
2947 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002948 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002949 }
2950
Robert Phillips4490d922020-03-03 14:50:59 -05002951 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2952 fUseScale, localMatrix);
2953
Brian Salomon8afde5f2020-04-01 16:22:00 -04002954 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002955 dstProxyView, gp, GrPrimitiveType::kTriangles,
Greg Daniel42dbca52020-11-20 10:22:43 -05002956 renderPassXferBarriers, colorLoadOp);
Robert Phillips4490d922020-03-03 14:50:59 -05002957 }
2958
Robert Phillips4490d922020-03-03 14:50:59 -05002959 void onPrepareDraws(Target* target) override {
2960 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002961 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002962 if (!fProgramInfo) {
2963 return;
2964 }
2965 }
joshualitt76e7fb62015-02-11 08:52:27 -08002966
bsalomonb5238a72015-05-05 07:49:49 -07002967 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002968 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002969 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2970 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002971
Brian Salomon12d22642019-01-29 14:38:50 -05002972 if (!indexBuffer) {
2973 SkDebugf("Could not allocate indices\n");
2974 return;
2975 }
Robert Phillips4490d922020-03-03 14:50:59 -05002976 PatternHelper helper(target, GrPrimitiveType::kTriangles,
Robert Phillips787fd9d2021-03-22 14:48:09 -04002977 fProgramInfo->geomProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002978 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002979 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002980 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002981 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002982 SkDebugf("Could not allocate vertices\n");
2983 return;
2984 }
2985
Brian Salomon05441c42017-05-15 16:45:49 -04002986 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002987 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002988 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002989 float reciprocalRadii[4] = {
2990 SkScalarInvert(rrect.fXRadius),
2991 SkScalarInvert(rrect.fYRadius),
2992 SkScalarInvert(rrect.fInnerXRadius),
2993 SkScalarInvert(rrect.fInnerYRadius)
2994 };
joshualitt76e7fb62015-02-11 08:52:27 -08002995
Brian Osmane3afdd52020-10-28 10:49:56 -04002996 // If the stroke width is exactly double the radius, the inner radii will be zero.
2997 // Pin to a large value, to avoid infinities in the shader. crbug.com/1139750
2998 reciprocalRadii[2] = std::min(reciprocalRadii[2], 1e6f);
2999 reciprocalRadii[3] = std::min(reciprocalRadii[3], 1e6f);
3000
joshualitt76e7fb62015-02-11 08:52:27 -08003001 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003002 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3003 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003004
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003005 SkScalar xMaxOffset = xOuterRadius;
3006 SkScalar yMaxOffset = yOuterRadius;
3007 if (!fStroked) {
3008 // For filled rrects we map a unit circle in the vertex attributes rather than
3009 // computing an ellipse and modifying that distance, so we normalize to 1.
3010 xMaxOffset /= rrect.fXRadius;
3011 yMaxOffset /= rrect.fYRadius;
3012 }
3013
Brian Salomon05441c42017-05-15 16:45:49 -04003014 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003015
Brian Salomon289e3d82016-12-14 15:52:56 -05003016 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3017 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003018 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003019 SK_ScalarNearlyZero, // we're using inversesqrt() in
3020 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003021 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003022
Brian Osman788b9162020-02-07 10:36:46 -05003023 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003024 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003025 verts.write(bounds.fLeft, yCoords[i],
3026 color,
3027 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003028 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003029 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003030
Brian Osmana1d4eb92018-12-06 16:33:10 -05003031 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3032 color,
3033 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003034 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003035 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003036
Brian Osmana1d4eb92018-12-06 16:33:10 -05003037 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3038 color,
3039 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003040 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003041 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003042
Brian Osmana1d4eb92018-12-06 16:33:10 -05003043 verts.write(bounds.fRight, yCoords[i],
3044 color,
3045 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003046 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003047 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003048 }
3049 }
Robert Phillips4490d922020-03-03 14:50:59 -05003050 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003051 }
3052
3053 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003054 if (!fProgramInfo || !fMesh) {
3055 return;
3056 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003057
Chris Dalton765ed362020-03-16 17:34:44 -06003058 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
Robert Phillips787fd9d2021-03-22 14:48:09 -04003059 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
Chris Dalton765ed362020-03-16 17:34:44 -06003060 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003061 }
3062
Herb Derbye25c3002020-10-27 15:57:27 -04003063 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003064 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003065
Brian Salomon05441c42017-05-15 16:45:49 -04003066 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003067 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003068 }
3069
bsalomoncdaa97b2016-03-08 08:30:14 -08003070 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003071 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003072 }
3073
Brian Salomon05441c42017-05-15 16:45:49 -04003074 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003075 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3076 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003077 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003078 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003079
Brian Salomon05441c42017-05-15 16:45:49 -04003080 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003081 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003082 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003083 }
3084
John Stilesaf366522020-08-13 09:57:34 -04003085#if GR_TEST_UTILS
3086 SkString onDumpInfo() const override {
3087 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3088 for (const auto& geo : fRRects) {
3089 string.appendf(
3090 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3091 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3092 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3093 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3094 geo.fInnerXRadius, geo.fInnerYRadius);
3095 }
3096 string += fHelper.dumpInfo();
3097 return string;
3098 }
3099#endif
3100
Brian Salomon05441c42017-05-15 16:45:49 -04003101 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003102 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003103 SkScalar fXRadius;
3104 SkScalar fYRadius;
3105 SkScalar fInnerXRadius;
3106 SkScalar fInnerYRadius;
3107 SkRect fDevBounds;
3108 };
3109
Brian Salomon289e3d82016-12-14 15:52:56 -05003110 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003111 Helper fHelper;
3112 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003113 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003114 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003115 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003116
Chris Daltoneb694b72020-03-16 09:25:50 -06003117 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003118 GrProgramInfo* fProgramInfo = nullptr;
3119
John Stiles7571f9e2020-09-02 22:42:33 -04003120 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003121};
3122
Herb Derbyc76d4092020-10-07 16:46:15 -04003123GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3124 GrPaint&& paint,
3125 const SkMatrix& viewMatrix,
3126 const SkRRect& rrect,
3127 const SkStrokeRec& stroke,
3128 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003129 SkASSERT(viewMatrix.rectStaysRect());
3130 SkASSERT(viewMatrix.isSimilarity());
3131 SkASSERT(rrect.isSimple());
3132 SkASSERT(!rrect.isOval());
3133 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3134
3135 // RRect ops only handle simple, but not too simple, rrects.
3136 // Do any matrix crunching before we reset the draw state for device coords.
3137 const SkRect& rrectBounds = rrect.getBounds();
3138 SkRect bounds;
3139 viewMatrix.mapRect(&bounds, rrectBounds);
3140
3141 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3142 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3143 viewMatrix[SkMatrix::kMSkewY]));
3144
3145 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3146 SkScalar scaledStroke = -1;
3147 SkScalar strokeWidth = stroke.getWidth();
3148 SkStrokeRec::Style style = stroke.getStyle();
3149
3150 bool isStrokeOnly =
3151 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3152 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3153
3154 if (hasStroke) {
3155 if (SkStrokeRec::kHairline_Style == style) {
3156 scaledStroke = SK_Scalar1;
3157 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003158 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3159 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003160 }
3161 }
3162
3163 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3164 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3165 // patch will have fractional coverage. This only matters when the interior is actually filled.
3166 // We could consider falling back to rect rendering here, since a tiny radius is
3167 // indistinguishable from a square corner.
3168 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3169 return nullptr;
3170 }
3171
3172 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3173 scaledStroke, isStrokeOnly);
3174}
3175
Herb Derbyc76d4092020-10-07 16:46:15 -04003176GrOp::Owner make_rrect_op(GrRecordingContext* context,
3177 GrPaint&& paint,
3178 const SkMatrix& viewMatrix,
3179 const SkRRect& rrect,
3180 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003181 SkASSERT(viewMatrix.rectStaysRect());
3182 SkASSERT(rrect.isSimple());
3183 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003184
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003185 // RRect ops only handle simple, but not too simple, rrects.
3186 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003187 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003188 SkRect bounds;
3189 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003190
Mike Reed242135a2018-02-22 13:41:39 -05003191 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003192 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3193 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3194 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3195 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003196
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003198
bsalomon4b4a7cc2016-07-08 04:42:54 -07003199 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3200 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003201 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003202
Brian Salomon289e3d82016-12-14 15:52:56 -05003203 bool isStrokeOnly =
3204 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003205 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3206
3207 if (hasStroke) {
3208 if (SkStrokeRec::kHairline_Style == style) {
3209 scaledStroke.set(1, 1);
3210 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003211 scaledStroke.fX = SkScalarAbs(
3212 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3213 scaledStroke.fY = SkScalarAbs(
3214 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003215 }
3216
Jim Van Verth64b85892019-06-17 12:01:46 -04003217 // if half of strokewidth is greater than radius, we don't handle that right now
3218 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3219 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003220 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003221 }
3222 }
3223
Brian Salomon8a97f562019-04-18 14:07:27 -04003224 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003225 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003226 std::swap(xRadius, yRadius);
3227 std::swap(scaledStroke.fX, scaledStroke.fY);
3228 }
3229
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003230 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3231 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3232 // patch will have fractional coverage. This only matters when the interior is actually filled.
3233 // We could consider falling back to rect rendering here, since a tiny radius is
3234 // indistinguishable from a square corner.
3235 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003236 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003237 }
3238
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003239 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003240 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3241 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003242}
3243
Herb Derbyc76d4092020-10-07 16:46:15 -04003244GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3245 GrPaint&& paint,
3246 const SkMatrix& viewMatrix,
3247 const SkRRect& rrect,
3248 const SkStrokeRec& stroke,
3249 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003250 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003251 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003252 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003253 }
3254
3255 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003256 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003257 }
3258
Greg Daniel2655ede2019-04-10 00:49:28 +00003259 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003260}
joshualitt3e708c52015-04-30 13:49:27 -07003261
bsalomon4b4a7cc2016-07-08 04:42:54 -07003262///////////////////////////////////////////////////////////////////////////////
3263
Herb Derbyc76d4092020-10-07 16:46:15 -04003264GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3265 GrPaint&& paint,
3266 const SkMatrix& viewMatrix,
3267 const SkRect& oval,
3268 const GrStyle& style,
3269 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003270 SkScalar width = oval.width();
3271 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3272 circle_stays_circle(viewMatrix));
3273
3274 auto r = width / 2.f;
3275 SkPoint center = { oval.centerX(), oval.centerY() };
3276 if (style.hasNonDashPathEffect()) {
3277 return nullptr;
3278 } else if (style.isDashed()) {
3279 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3280 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3281 return nullptr;
3282 }
3283 auto onInterval = style.dashIntervals()[0];
3284 auto offInterval = style.dashIntervals()[1];
3285 if (offInterval == 0) {
3286 GrStyle strokeStyle(style.strokeRec(), nullptr);
3287 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3288 strokeStyle, shaderCaps);
3289 } else if (onInterval == 0) {
3290 // There is nothing to draw but we have no way to indicate that here.
3291 return nullptr;
3292 }
3293 auto angularOnInterval = onInterval / r;
3294 auto angularOffInterval = offInterval / r;
3295 auto phaseAngle = style.dashPhase() / r;
3296 // Currently this function doesn't accept ovals with different start angles, though
3297 // it could.
3298 static const SkScalar kStartAngle = 0.f;
3299 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3300 style.strokeRec().getWidth(), kStartAngle,
3301 angularOnInterval, angularOffInterval, phaseAngle);
3302 }
3303 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3304}
3305
Herb Derbyc76d4092020-10-07 16:46:15 -04003306GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3307 GrPaint&& paint,
3308 const SkMatrix& viewMatrix,
3309 const SkRect& oval,
3310 const GrStyle& style,
3311 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003312 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003313 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003314 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3315 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003316 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003317 }
3318
3319 if (style.pathEffect()) {
3320 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003321 }
3322
Stan Ilieveb868aa2017-02-21 11:06:16 -05003323 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003324 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003325 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003326 }
3327
Stan Ilieveb868aa2017-02-21 11:06:16 -05003328 // Otherwise, if we have shader derivative support, render as device-independent
3329 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003330 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3331 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3332 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3333 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3334 // Check for near-degenerate matrix
3335 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003336 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003337 style.strokeRec());
3338 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003339 }
3340
bsalomon4b4a7cc2016-07-08 04:42:54 -07003341 return nullptr;
3342}
3343
3344///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003345
Herb Derbyc76d4092020-10-07 16:46:15 -04003346GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3347 GrPaint&& paint,
3348 const SkMatrix& viewMatrix,
3349 const SkRect& oval, SkScalar startAngle,
3350 SkScalar sweepAngle, bool useCenter,
3351 const GrStyle& style,
3352 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003353 SkASSERT(!oval.isEmpty());
3354 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003355 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003356 if (SkScalarAbs(sweepAngle) >= 360.f) {
3357 return nullptr;
3358 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003359 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3360 return nullptr;
3361 }
3362 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003363 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3364 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003365 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003366 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003367}
3368
3369///////////////////////////////////////////////////////////////////////////////
3370
Hal Canary6f6961e2017-01-31 13:50:44 -05003371#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003372
Brian Salomon05441c42017-05-15 16:45:49 -04003373GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003374 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003375 SkScalar rotate = random->nextSScalar1() * 360.f;
3376 SkScalar translateX = random->nextSScalar1() * 1000.f;
3377 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003378 SkScalar scale;
3379 do {
3380 scale = random->nextSScalar1() * 100.f;
3381 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003382 SkMatrix viewMatrix;
3383 viewMatrix.setRotate(rotate);
3384 viewMatrix.postTranslate(translateX, translateY);
3385 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003386 SkRect circle = GrTest::TestSquare(random);
3387 SkPoint center = {circle.centerX(), circle.centerY()};
3388 SkScalar radius = circle.width() / 2.f;
3389 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003390 CircleOp::ArcParams arcParamsTmp;
3391 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003392 if (random->nextBool()) {
3393 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003394 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3395 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003396 arcParams = &arcParamsTmp;
3397 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003398 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3399 center, radius,
3400 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003401 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003402 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003403 }
Mike Klein16885072018-12-11 09:54:31 -05003404 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003405 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003406}
3407
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003408GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3409 SkScalar rotate = random->nextSScalar1() * 360.f;
3410 SkScalar translateX = random->nextSScalar1() * 1000.f;
3411 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003412 SkScalar scale;
3413 do {
3414 scale = random->nextSScalar1() * 100.f;
3415 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003416 SkMatrix viewMatrix;
3417 viewMatrix.setRotate(rotate);
3418 viewMatrix.postTranslate(translateX, translateY);
3419 viewMatrix.postScale(scale, scale);
3420 SkRect circle = GrTest::TestSquare(random);
3421 SkPoint center = {circle.centerX(), circle.centerY()};
3422 SkScalar radius = circle.width() / 2.f;
3423 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3424 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3425 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3426 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3427 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003428 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3429 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003430 startAngle, onAngle, offAngle, phase);
3431}
3432
Brian Salomon05441c42017-05-15 16:45:49 -04003433GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003434 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003435 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003436 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003437 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003438}
3439
Brian Salomon05441c42017-05-15 16:45:49 -04003440GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003441 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003442 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003443 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003444 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003445}
3446
Jim Van Verth64b85892019-06-17 12:01:46 -04003447GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3448 do {
3449 SkScalar rotate = random->nextSScalar1() * 360.f;
3450 SkScalar translateX = random->nextSScalar1() * 1000.f;
3451 SkScalar translateY = random->nextSScalar1() * 1000.f;
3452 SkScalar scale;
3453 do {
3454 scale = random->nextSScalar1() * 100.f;
3455 } while (scale == 0);
3456 SkMatrix viewMatrix;
3457 viewMatrix.setRotate(rotate);
3458 viewMatrix.postTranslate(translateX, translateY);
3459 viewMatrix.postScale(scale, scale);
3460 SkRect rect = GrTest::TestRect(random);
3461 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3462 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3463 if (rrect.isOval()) {
3464 continue;
3465 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003466 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003467 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3468 GrTest::TestStrokeRec(random), nullptr);
3469 if (op) {
3470 return op;
3471 }
3472 assert_alive(paint);
3473 } while (true);
3474}
3475
Brian Salomon05441c42017-05-15 16:45:49 -04003476GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003477 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003478 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003479 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003480 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003481}
3482
3483#endif