blob: a6b298f947352358b975b0e4d8023c2d0122d493 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStrokeRec.h"
Michael Ludwigfbe28592020-06-26 16:02:15 -04009#include "src/core/SkMatrixPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkRRectPriv.h"
11#include "src/gpu/GrCaps.h"
12#include "src/gpu/GrDrawOpTest.h"
13#include "src/gpu/GrGeometryProcessor.h"
14#include "src/gpu/GrOpFlushState.h"
15#include "src/gpu/GrProcessor.h"
Robert Phillips4490d922020-03-03 14:50:59 -050016#include "src/gpu/GrProgramInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "src/gpu/GrResourceProvider.h"
18#include "src/gpu/GrShaderCaps.h"
19#include "src/gpu/GrStyle.h"
20#include "src/gpu/GrVertexWriter.h"
21#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
23#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
24#include "src/gpu/glsl/GrGLSLUniformHandler.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/gpu/glsl/GrGLSLVarying.h"
26#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
27#include "src/gpu/ops/GrMeshDrawOp.h"
28#include "src/gpu/ops/GrOvalOpFactory.h"
29#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080030
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080034
Brian Salomon289e3d82016-12-14 15:52:56 -050035static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050036
Brian Osman2b6e3902018-11-21 15:29:43 -050037// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
38static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
39 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
40};
41
John Stilesa6841be2020-08-06 14:11:56 -040042} // namespace
commit-bot@chromium.org81312832013-03-22 18:34:09 +000043
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
bsalomonce1c8862014-12-15 07:11:22 -080047 * The output of this effect is a modulation of the input color and coverage for a circle. It
48 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080049 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080050 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080051 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080052 * vec4f : (p.xy, outerRad, innerRad)
53 * p is the position in the normalized space.
54 * outerRad is the outerRadius in device space.
55 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070056 * Additional clip planes are supported for rendering circular arcs. The additional planes are
57 * either intersected or unioned together. Up to three planes are supported (an initial plane,
58 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050059 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040061 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
62 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 */
64
bsalomoncdaa97b2016-03-08 08:30:14 -080065class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -050067 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
68 bool isectPlane, bool unionPlane, bool roundCaps,
69 bool wideColor, const SkMatrix& localMatrix) {
70 return arena->make<CircleGeometryProcessor>(stroke, clipPlane, isectPlane, unionPlane,
71 roundCaps, wideColor, localMatrix);
72 }
73
74 const char* name() const override { return "CircleGeometryProcessor"; }
75
76 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
77 GLSLProcessor::GenKey(*this, caps, b);
78 }
79
80 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
81 return new GLSLProcessor();
82 }
83
84private:
85 friend class ::SkArenaAlloc; // for access to ctor
86
Greg Daniel2655ede2019-04-10 00:49:28 +000087 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050088 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040089 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040090 , fLocalMatrix(localMatrix)
91 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050092 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050093 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050094 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
95
bsalomon4f3a0ca2016-08-22 13:14:26 -070096 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040097 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070098 }
99 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400100 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700101 }
102 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -0400103 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700104 }
Brian Salomon45c92202018-04-10 10:53:58 -0400105 if (roundCaps) {
106 SkASSERT(stroke);
107 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -0400108 fInRoundCapCenters =
109 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -0400110 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500111 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 }
113
egdaniel57d3b032015-11-13 11:57:27 -0800114 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000115 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800116 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117
Brian Salomon289e3d82016-12-14 15:52:56 -0500118 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800119 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800120 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800121 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800122 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700123 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800124
joshualittabb52a12015-01-13 15:02:10 -0800125 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400127 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500128 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400129 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400130 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
132 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400133 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400134 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
136 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400137 if (cgp.fInUnionPlane.isInitialized()) {
138 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400139 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700140 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
141 }
Brian Salomon45c92202018-04-10 10:53:58 -0400142 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400143 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400144 fragBuilder->codeAppend("float4 roundCapCenters;");
145 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
146 varyingHandler->addVarying("capRadius", &capRadius,
147 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
148 // This is the cap radius in normalized space where the outer radius is 1 and
149 // circledEdge.w is the normalized inner radius.
150 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400152 }
joshualittabb52a12015-01-13 15:02:10 -0800153
joshualittb8c241a2015-05-19 08:23:30 -0700154 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500155 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800156
joshualittabb52a12015-01-13 15:02:10 -0800157 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500158 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400159 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
160 cgp.fInPosition.asShaderVar(), cgp.fLocalMatrix,
161 &fLocalMatrixUniform);
joshualitt4973d9d2014-11-08 09:24:25 -0800162
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400163 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500164 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000165 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800166 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500167 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500168 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Greg Daniel2655ede2019-04-10 00:49:28 +0000169 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
egdaniel4ca2e602015-11-18 08:01:26 -0800170 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000172
Brian Salomon92be2f72018-06-19 14:33:47 -0400173 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500174 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000175 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
176 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400177 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000179 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
180 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700181 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400182 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Greg Daniel2655ede2019-04-10 00:49:28 +0000184 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
185 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 }
187 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400188 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400189 // We compute coverage of the round caps as circles at the butt caps produced
190 // by the clip planes. The inverse of the clip planes is applied so that there
191 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400192 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
194 " roundCapCenters.xy)));"
195 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
196 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400197 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400198 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
199 capRadius.fsIn(), capRadius.fsIn());
200 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700201 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000202 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 }
204
robertphillips46d36f02015-01-18 08:14:14 -0800205 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500206 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700207 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800208 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400209 uint32_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500210 key = cgp.fStroke ? 0x01 : 0x0;
Michael Ludwig553db622020-06-19 10:47:30 -0400211 key |= cgp.fInClipPlane.isInitialized() ? 0x02 : 0x0;
212 key |= cgp.fInIsectPlane.isInitialized() ? 0x04 : 0x0;
213 key |= cgp.fInUnionPlane.isInitialized() ? 0x08 : 0x0;
214 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x10 : 0x0;
215 key |= (ComputeMatrixKey(cgp.fLocalMatrix) << 16);
joshualittb8c241a2015-05-19 08:23:30 -0700216 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217 }
218
Brian Osman609f1592020-07-01 15:14:39 -0400219 void setData(const GrGLSLProgramDataManager& pdman,
220 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400221 this->setTransform(pdman, fLocalMatrixUniform,
222 primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
223 &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700224 }
225
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400227 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400228
229 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
230 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 };
232
Brian Salomon289e3d82016-12-14 15:52:56 -0500233 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400234
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500235 Attribute fInPosition;
236 Attribute fInColor;
237 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400238 // Optional attributes.
239 Attribute fInClipPlane;
240 Attribute fInIsectPlane;
241 Attribute fInUnionPlane;
242 Attribute fInRoundCapCenters;
243
Brian Salomon289e3d82016-12-14 15:52:56 -0500244 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400245 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246
John Stiles7571f9e2020-09-02 22:42:33 -0400247 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248};
249
bsalomoncdaa97b2016-03-08 08:30:14 -0800250GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
Hal Canary6f6961e2017-01-31 13:50:44 -0500252#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500253GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400254 bool stroke = d->fRandom->nextBool();
255 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500256 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400257 bool clipPlane = d->fRandom->nextBool();
258 bool isectPlane = d->fRandom->nextBool();
259 bool unionPlane = d->fRandom->nextBool();
260 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
262 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263}
Hal Canary6f6961e2017-01-31 13:50:44 -0500264#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400266class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
267public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500268 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
269 const SkMatrix& localMatrix) {
270 return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400271 }
272
273 ~ButtCapDashedCircleGeometryProcessor() override {}
274
275 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
276
277 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
278 GLSLProcessor::GenKey(*this, caps, b);
279 }
280
281 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
282 return new GLSLProcessor();
283 }
284
285private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500286 friend class ::SkArenaAlloc; // for access to ctor
287
288 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
289 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
290 , fLocalMatrix(localMatrix) {
291 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
292 fInColor = MakeColorAttribute("inColor", wideColor);
293 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
295 this->setVertexAttributes(&fInPosition, 4);
296 }
297
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298 class GLSLProcessor : public GrGLSLGeometryProcessor {
299 public:
300 GLSLProcessor() {}
301
302 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
303 const ButtCapDashedCircleGeometryProcessor& bcscgp =
304 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
305 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
306 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
307 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
308 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
309
310 // emit attributes
311 varyingHandler->emitAttributes(bcscgp);
312 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500313 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314
315 fragBuilder->codeAppend("float4 dashParams;");
316 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500317 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400318 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
319 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
320 varyingHandler->addVarying("wrapDashes", &wrapDashes,
321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
322 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
323 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
324 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
327 // x = length of on interval, y = length of on + off.
328 // There are two other parameters in dashParams.zw:
329 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
330 // Each interval has a "corresponding" dash which may be shifted partially or
331 // fully out of its interval by the phase. So there may be up to two "visual"
332 // dashes in an interval.
333 // When computing coverage in an interval we look at three dashes. These are the
334 // "corresponding" dashes from the current, previous, and next intervals. Any of these
335 // may be phase shifted into our interval or even when phase=0 they may be within half a
336 // pixel distance of a pixel center in the interval.
337 // When in the first interval we need to check the dash from the last interval. And
338 // similarly when in the last interval we need to check the dash from the first
339 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
340 // We compute the dash begin/end angles in the vertex shader and apply them in the
341 // fragment shader when we detect we're in the first/last interval.
342 vertBuilder->codeAppend(R"(
343 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
344 // to the fragment shader as a varying.
345 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500346 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400347 // We can happen to be perfectly divisible.
348 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500349 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400350 }
351 // Let 'l' be the last interval before reaching 2 pi.
352 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
353 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
354 // interval.
355 half offset = 0;
356 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500359 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400360 }
361 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
362 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
363 // min.
364 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
365
366 // Based on the phase determine whether the -1st, 0th, or 1st interval's
367 // "corresponding" dash appears in the 0th interval and is closest to l.
368 offset = 0;
369 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500370 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400371 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500372 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400373 }
374 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
375 wrapDashes.w = wrapDashes.z + dashParams.x;
376 // The start of the dash we're considering may be clipped by the start of the
377 // circle.
378 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
379 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
382 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
383 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
384
385 // setup pass through color
386 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500387 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
389
390 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500391 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400392 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
393 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
394 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
John Stiles6b58a332020-10-26 17:53:06 -0400400 SkString fnName = fragBuilder->getMangledFunctionName("coverage_from_dash_edge");
401 fragBuilder->emitFunction(kFloat_GrSLType, fnName.c_str(),
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400402 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400406 return saturate(linearDist + 0.5);
John Stiles6b58a332020-10-26 17:53:06 -0400407 )");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 fragBuilder->codeAppend(R"(
409 float d = length(circleEdge.xy) * circleEdge.z;
410
411 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500412 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400413 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500414 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400415 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400416 edgeAlpha *= innerAlpha;
417
Ethan Nicholase1f55022019-02-05 17:17:40 -0500418 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400419 angleFromStart = mod(angleFromStart, 6.28318530718);
420 float x = mod(angleFromStart, dashParams.y);
421 // Convert the radial distance from center to pixel into a diameter.
422 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500423 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
424 half(dashParams.w));
425 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
426 half(dashParams.y) + half(dashParams.x) -
427 half(dashParams.w));
428 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
429 half(-dashParams.y) + half(dashParams.x) -
430 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400431 half dashAlpha = 0;
432 )");
433 fragBuilder->codeAppendf(R"(
434 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500435 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400436 currDash.y = min(currDash.y, lastIntervalLength);
437 if (nextDash.x >= lastIntervalLength) {
438 // The next dash is outside the 0..2pi range, throw it away
439 nextDash.xy = half2(1000);
440 } else {
441 // Clip the end of the next dash to the end of the circle
442 nextDash.y = min(nextDash.y, lastIntervalLength);
443 }
444 }
445 )", fnName.c_str(), fnName.c_str());
446 fragBuilder->codeAppendf(R"(
447 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500448 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400449 currDash.x = max(currDash.x, 0);
450 if (prevDash.y <= 0) {
451 // The previous dash is outside the 0..2pi range, throw it away
452 prevDash.xy = half2(1000);
453 } else {
454 // Clip the start previous dash to the start of the circle
455 prevDash.x = max(prevDash.x, 0);
456 }
457 }
458 )", fnName.c_str(), fnName.c_str());
459 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500460 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
461 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
462 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400463 dashAlpha = min(dashAlpha, 1);
464 edgeAlpha *= dashAlpha;
465 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
466 fnName.c_str());
467 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
468 }
469
470 static void GenKey(const GrGeometryProcessor& gp,
471 const GrShaderCaps&,
472 GrProcessorKeyBuilder* b) {
473 const ButtCapDashedCircleGeometryProcessor& bcscgp =
474 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400475 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400476 }
477
Brian Osman609f1592020-07-01 15:14:39 -0400478 void setData(const GrGLSLProgramDataManager& pdman,
479 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400480 this->setTransform(pdman, fLocalMatrixUniform,
481 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
482 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400483 }
484
485 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400486 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400487
488 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
489 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400490 };
491
492 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500493 Attribute fInPosition;
494 Attribute fInColor;
495 Attribute fInCircleEdge;
496 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400497
498 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
499
John Stiles7571f9e2020-09-02 22:42:33 -0400500 using INHERITED = GrGeometryProcessor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400501};
502
503#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500504GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500505 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400506 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500507 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400508}
509#endif
510
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000511///////////////////////////////////////////////////////////////////////////////
512
513/**
514 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000515 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
516 * in both x and y directions.
517 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000518 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000519 */
520
bsalomoncdaa97b2016-03-08 08:30:14 -0800521class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000522public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500523 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
524 bool useScale, const SkMatrix& localMatrix) {
525 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
526 }
527
528 ~EllipseGeometryProcessor() override {}
529
530 const char* name() const override { return "EllipseGeometryProcessor"; }
531
532 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
533 GLSLProcessor::GenKey(*this, caps, b);
534 }
535
536 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
537 return new GLSLProcessor();
538 }
539
540private:
541 friend class ::SkArenaAlloc; // for access to ctor
542
Greg Daniel2655ede2019-04-10 00:49:28 +0000543 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400544 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500545 : INHERITED(kEllipseGeometryProcessor_ClassID)
546 , fLocalMatrix(localMatrix)
547 , fStroke(stroke)
548 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500549 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500550 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400551 if (useScale) {
552 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
553 } else {
554 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
555 }
556 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500557 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000558 }
559
egdaniel57d3b032015-11-13 11:57:27 -0800560 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000561 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800562 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000563
Brian Salomon289e3d82016-12-14 15:52:56 -0500564 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800565 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800566 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800567 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800568 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000569
joshualittabb52a12015-01-13 15:02:10 -0800570 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800571 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800572
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400573 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
574 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800575 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800576 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500577 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000578
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400579 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800580 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800582
Chris Dalton60283612018-02-14 13:38:14 -0700583 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700584 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500585 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800586
joshualittabb52a12015-01-13 15:02:10 -0800587 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500588 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400589 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
590 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
591 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800592
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400593 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
594 // to compute both the edges because we need two separate test equations for
595 // the single offset.
596 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
597 // the distance by the gradient, non-uniformly scaled by the inverse of the
598 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800599
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400600 // On medium precision devices, we scale the denominator of the distance equation
601 // before taking the inverse square root to minimize the chance that we're dividing
602 // by zero, then we scale the result back.
603
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000604 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400605 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400606 if (egp.fStroke) {
607 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
608 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400609 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
610 if (egp.fUseScale) {
611 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
612 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
613 } else {
614 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
615 }
616 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700617
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000618 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400619 if (args.fShaderCaps->floatIs32Bits()) {
620 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
621 } else {
622 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
623 }
624 if (egp.fUseScale) {
625 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
626 ellipseOffsets.fsIn());
627 } else {
628 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
629 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000630 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000631
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000632 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800633 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400634 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800635 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400636 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400637 if (egp.fUseScale) {
638 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
639 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
640 } else {
641 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
642 }
643 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
644 if (!args.fShaderCaps->floatIs32Bits()) {
645 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
646 }
647 if (egp.fUseScale) {
648 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
649 ellipseOffsets.fsIn());
650 } else {
651 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
652 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000653 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654 }
655
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400656 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000657 }
658
robertphillips46d36f02015-01-18 08:14:14 -0800659 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500660 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700661 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800662 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400663 uint32_t key = egp.fStroke ? 0x1 : 0x0;
664 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700665 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000666 }
667
Brian Osman609f1592020-07-01 15:14:39 -0400668 void setData(const GrGLSLProgramDataManager& pdman,
669 const GrPrimitiveProcessor& primProc) override {
bsalomona624bf32016-09-20 09:12:47 -0700670 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400671 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700672 }
673
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000674 private:
John Stiles7571f9e2020-09-02 22:42:33 -0400675 using INHERITED = GrGLSLGeometryProcessor;
Michael Ludwig553db622020-06-19 10:47:30 -0400676
677 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
678 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000679 };
680
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500681 Attribute fInPosition;
682 Attribute fInColor;
683 Attribute fInEllipseOffset;
684 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400685
joshualitte3ababe2015-05-15 07:56:07 -0700686 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000687 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400688 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000689
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400690 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000691
John Stiles7571f9e2020-09-02 22:42:33 -0400692 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000693};
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
Hal Canary6f6961e2017-01-31 13:50:44 -0500697#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500698GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
699 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
700 d->fRandom->nextBool(), d->fRandom->nextBool(),
701 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000702}
Hal Canary6f6961e2017-01-31 13:50:44 -0500703#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000704
705///////////////////////////////////////////////////////////////////////////////
706
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000707/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000708 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000709 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
710 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
711 * using differentials.
712 *
713 * The result is device-independent and can be used with any affine matrix.
714 */
715
bsalomoncdaa97b2016-03-08 08:30:14 -0800716enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000717
bsalomoncdaa97b2016-03-08 08:30:14 -0800718class DIEllipseGeometryProcessor : public GrGeometryProcessor {
719public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500720 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
721 const SkMatrix& viewMatrix, DIEllipseStyle style) {
722 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
723 }
724
725 ~DIEllipseGeometryProcessor() override {}
726
727 const char* name() const override { return "DIEllipseGeometryProcessor"; }
728
729 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
730 GLSLProcessor::GenKey(*this, caps, b);
731 }
732
733 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
734 return new GLSLProcessor();
735 }
736
737private:
738 friend class ::SkArenaAlloc; // for access to ctor
739
Greg Daniel2655ede2019-04-10 00:49:28 +0000740 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400741 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400742 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400743 , fViewMatrix(viewMatrix)
744 , fUseScale(useScale)
745 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500746 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500747 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400748 if (useScale) {
749 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
750 kFloat3_GrSLType};
751 } else {
752 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
753 kFloat2_GrSLType};
754 }
755 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500756 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000757 }
758
egdaniel57d3b032015-11-13 11:57:27 -0800759 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500761 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762
joshualitt465283c2015-09-11 08:19:35 -0700763 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800764 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800765 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800766 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800767 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000768
joshualittabb52a12015-01-13 15:02:10 -0800769 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800770 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800771
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400772 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
773 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800774 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500775 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700776
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400777 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800778 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800780
Chris Dalton60283612018-02-14 13:38:14 -0700781 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500782 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800783
joshualittabb52a12015-01-13 15:02:10 -0800784 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400785 this->writeOutputPosition(vertBuilder,
786 uniformHandler,
787 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500788 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400789 diegp.fViewMatrix,
790 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400791 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800792
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400794 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
795 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
796 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
797 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500798 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400799 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
800 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400802 if (diegp.fUseScale) {
803 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
804 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000805
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400806 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000807 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400808 if (args.fShaderCaps->floatIs32Bits()) {
809 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
810 } else {
811 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
812 }
813 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
814 if (diegp.fUseScale) {
815 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
816 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800817 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000818 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000819 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
820 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000821 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000822 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000823 }
824
825 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800826 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800827 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
828 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400829 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
830 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500831 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400832 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
833 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500834 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400835 if (diegp.fUseScale) {
836 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
837 }
838 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
839 if (!args.fShaderCaps->floatIs32Bits()) {
840 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
841 }
842 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
843 if (diegp.fUseScale) {
844 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
845 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000846 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000847 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000848
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400849 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000850 }
851
robertphillips46d36f02015-01-18 08:14:14 -0800852 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500853 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700854 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800855 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400856 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
857 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700858 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000859 }
860
Brian Osman609f1592020-07-01 15:14:39 -0400861 void setData(const GrGLSLProgramDataManager& pdman,
862 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800863 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700864
Michael Ludwig553db622020-06-19 10:47:30 -0400865 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000866 }
867
868 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400869 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700870 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800871
John Stiles7571f9e2020-09-02 22:42:33 -0400872 using INHERITED = GrGLSLGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000873 };
874
Brian Salomon92be2f72018-06-19 14:33:47 -0400875
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500876 Attribute fInPosition;
877 Attribute fInColor;
878 Attribute fInEllipseOffsets0;
879 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400880
Brian Salomon289e3d82016-12-14 15:52:56 -0500881 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400882 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500883 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000884
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400885 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000886
John Stiles7571f9e2020-09-02 22:42:33 -0400887 using INHERITED = GrGeometryProcessor;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000888};
889
bsalomoncdaa97b2016-03-08 08:30:14 -0800890GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000891
Hal Canary6f6961e2017-01-31 13:50:44 -0500892#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500893GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
894 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
895 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
896 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000897}
Hal Canary6f6961e2017-01-31 13:50:44 -0500898#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000899
900///////////////////////////////////////////////////////////////////////////////
901
jvanverth6ca48822016-10-07 06:57:32 -0700902// We have two possible cases for geometry for a circle:
903
904// In the case of a normal fill, we draw geometry for the circle as an octagon.
905static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500906 // enter the octagon
907 // clang-format off
908 0, 1, 8, 1, 2, 8,
909 2, 3, 8, 3, 4, 8,
910 4, 5, 8, 5, 6, 8,
911 6, 7, 8, 7, 0, 8
912 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700913};
914
915// For stroked circles, we use two nested octagons.
916static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500917 // enter the octagon
918 // clang-format off
919 0, 1, 9, 0, 9, 8,
920 1, 2, 10, 1, 10, 9,
921 2, 3, 11, 2, 11, 10,
922 3, 4, 12, 3, 12, 11,
923 4, 5, 13, 4, 13, 12,
924 5, 6, 14, 5, 14, 13,
925 6, 7, 15, 6, 15, 14,
926 7, 0, 8, 7, 8, 15,
927 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700928};
929
Brian Osman9d958b52018-11-13 12:46:56 -0500930// Normalized geometry for octagons that circumscribe and lie on a circle:
931
932static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
933static constexpr SkPoint kOctagonOuter[] = {
934 SkPoint::Make(-kOctOffset, -1),
935 SkPoint::Make( kOctOffset, -1),
936 SkPoint::Make( 1, -kOctOffset),
937 SkPoint::Make( 1, kOctOffset),
938 SkPoint::Make( kOctOffset, 1),
939 SkPoint::Make(-kOctOffset, 1),
940 SkPoint::Make(-1, kOctOffset),
941 SkPoint::Make(-1, -kOctOffset),
942};
943
944// cosine and sine of pi/8
945static constexpr SkScalar kCosPi8 = 0.923579533f;
946static constexpr SkScalar kSinPi8 = 0.382683432f;
947static constexpr SkPoint kOctagonInner[] = {
948 SkPoint::Make(-kSinPi8, -kCosPi8),
949 SkPoint::Make( kSinPi8, -kCosPi8),
950 SkPoint::Make( kCosPi8, -kSinPi8),
951 SkPoint::Make( kCosPi8, kSinPi8),
952 SkPoint::Make( kSinPi8, kCosPi8),
953 SkPoint::Make(-kSinPi8, kCosPi8),
954 SkPoint::Make(-kCosPi8, kSinPi8),
955 SkPoint::Make(-kCosPi8, -kSinPi8),
956};
Brian Salomon289e3d82016-12-14 15:52:56 -0500957
jvanverth6ca48822016-10-07 06:57:32 -0700958static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
959static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
960static const int kVertsPerStrokeCircle = 16;
961static const int kVertsPerFillCircle = 9;
962
963static int circle_type_to_vert_count(bool stroked) {
964 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
965}
966
967static int circle_type_to_index_count(bool stroked) {
968 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
969}
970
971static const uint16_t* circle_type_to_indices(bool stroked) {
972 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
973}
974
975///////////////////////////////////////////////////////////////////////////////
976
Brian Salomon05441c42017-05-15 16:45:49 -0400977class CircleOp final : public GrMeshDrawOp {
978private:
979 using Helper = GrSimpleMeshDrawOpHelper;
980
joshualitt76e7fb62015-02-11 08:52:27 -0800981public:
Brian Salomon25a88092016-12-01 09:36:50 -0500982 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700983
bsalomon4f3a0ca2016-08-22 13:14:26 -0700984 /** Optional extra params to render a partial arc rather than a full circle. */
985 struct ArcParams {
986 SkScalar fStartAngleRadians;
987 SkScalar fSweepAngleRadians;
988 bool fUseCenter;
989 };
Brian Salomon05441c42017-05-15 16:45:49 -0400990
Herb Derbyc76d4092020-10-07 16:46:15 -0400991 static GrOp::Owner Make(GrRecordingContext* context,
992 GrPaint&& paint,
993 const SkMatrix& viewMatrix,
994 SkPoint center,
995 SkScalar radius,
996 const GrStyle& style,
997 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 if (style.hasPathEffect()) {
1000 return nullptr;
1001 }
Brian Salomon05441c42017-05-15 16:45:49 -04001002 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001003 SkStrokeRec::Style recStyle = stroke.getStyle();
1004 if (arcParams) {
1005 // Arc support depends on the style.
1006 switch (recStyle) {
1007 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001008 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001009 return nullptr;
1010 case SkStrokeRec::kFill_Style:
1011 // This supports all fills.
1012 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001013 case SkStrokeRec::kStroke_Style:
1014 // Strokes that don't use the center point are supported with butt and round
1015 // caps.
1016 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1017 return nullptr;
1018 }
1019 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001021 // Hairline only supports butt cap. Round caps could be emulated by slightly
1022 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001023 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1024 return nullptr;
1025 }
1026 break;
1027 }
1028 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001029 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1030 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001031 }
1032
Herb Derbyc76d4092020-10-07 16:46:15 -04001033 CircleOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001034 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1035 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001036 : GrMeshDrawOp(ClassID())
Herb Derbyc76d4092020-10-07 16:46:15 -04001037 , fHelper(processorSet, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001038 const SkStrokeRec& stroke = style.strokeRec();
1039 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001040
Brian Salomon45c92202018-04-10 10:53:58 -04001041 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001042
bsalomon4b4a7cc2016-07-08 04:42:54 -07001043 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001044 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001045 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001046
Brian Salomon289e3d82016-12-14 15:52:56 -05001047 bool isStrokeOnly =
1048 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001049 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001050
jvanverth6ca48822016-10-07 06:57:32 -07001051 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001052 SkScalar outerRadius = radius;
1053 SkScalar halfWidth = 0;
1054 if (hasStroke) {
1055 if (SkScalarNearlyZero(strokeWidth)) {
1056 halfWidth = SK_ScalarHalf;
1057 } else {
1058 halfWidth = SkScalarHalf(strokeWidth);
1059 }
1060
1061 outerRadius += halfWidth;
1062 if (isStrokeOnly) {
1063 innerRadius = radius - halfWidth;
1064 }
1065 }
1066
1067 // The radii are outset for two reasons. First, it allows the shader to simply perform
1068 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1069 // Second, the outer radius is used to compute the verts of the bounding box that is
1070 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001071 outerRadius += SK_ScalarHalf;
1072 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001073 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001074 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001075
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 // This makes every point fully inside the intersection plane.
1077 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1078 // This makes every point fully outside the union plane.
1079 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001080 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001081 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1082 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001083 if (arcParams) {
1084 // The shader operates in a space where the circle is translated to be centered at the
1085 // origin. Here we compute points on the unit circle at the starting and ending angles.
1086 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001087 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1088 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001089 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001090 stopPoint.fY = SkScalarSin(endAngle);
1091 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001092
1093 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1094 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1095 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1096 startPoint.normalize();
1097 stopPoint.normalize();
1098
Brian Salomon3517aa72019-12-11 08:16:22 -05001099 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1100 // should orient the clip planes for arcs.
1101 SkASSERT(viewMatrix.isSimilarity());
1102 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1103 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1104 if (upperLeftDet < 0) {
1105 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001106 }
1107
Brian Salomon45c92202018-04-10 10:53:58 -04001108 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1109 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1110 SkPoint roundCaps[2];
1111 if (fRoundCaps) {
1112 // Compute the cap center points in the normalized space.
1113 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1114 roundCaps[0] = startPoint * midRadius;
1115 roundCaps[1] = stopPoint * midRadius;
1116 } else {
1117 roundCaps[0] = kUnusedRoundCaps[0];
1118 roundCaps[1] = kUnusedRoundCaps[1];
1119 }
1120
bsalomon4f3a0ca2016-08-22 13:14:26 -07001121 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001122 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1123 // center of the butts.
1124 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001126 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001128 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1129 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1130 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001131 if (useCenter) {
1132 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1133 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001134 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1135 if (arcParams->fSweepAngleRadians < 0) {
1136 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001137 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001138 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001139 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001140 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001141 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 color,
1143 innerRadius,
1144 outerRadius,
1145 {norm0.fX, norm0.fY, 0.5f},
1146 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1147 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001148 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001149 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001150 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001151 fClipPlaneIsect = false;
1152 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001153 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001154 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001155 color,
1156 innerRadius,
1157 outerRadius,
1158 {norm0.fX, norm0.fY, 0.5f},
1159 {norm1.fX, norm1.fY, 0.5f},
1160 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001161 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001162 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001163 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001164 fClipPlaneIsect = true;
1165 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001166 }
1167 } else {
1168 // We clip to a secant of the original circle.
1169 startPoint.scale(radius);
1170 stopPoint.scale(radius);
1171 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1172 norm.normalize();
1173 if (arcParams->fSweepAngleRadians > 0) {
1174 norm.negate();
1175 }
1176 SkScalar d = -norm.dot(startPoint) + 0.5f;
1177
Brian Salomon05441c42017-05-15 16:45:49 -04001178 fCircles.emplace_back(
1179 Circle{color,
1180 innerRadius,
1181 outerRadius,
1182 {norm.fX, norm.fY, d},
1183 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1184 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001185 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001186 devBounds,
1187 stroked});
1188 fClipPlane = true;
1189 fClipPlaneIsect = false;
1190 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001191 }
1192 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001193 fCircles.emplace_back(
1194 Circle{color,
1195 innerRadius,
1196 outerRadius,
1197 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001200 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001201 devBounds,
1202 stroked});
1203 fClipPlane = false;
1204 fClipPlaneIsect = false;
1205 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001206 }
bsalomon88cf17d2016-07-08 06:40:56 -07001207 // Use the original radius and stroke radius for the bounds so that it does not include the
1208 // AA bloat.
1209 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001210 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001211 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001212 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001213 fVertCount = circle_type_to_vert_count(stroked);
1214 fIndexCount = circle_type_to_index_count(stroked);
1215 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001216 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001217
Brian Salomon289e3d82016-12-14 15:52:56 -05001218 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001219
Chris Dalton1706cbf2019-05-21 19:35:29 -06001220 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001221 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001222 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001223 } else {
1224 fHelper.visitProxies(func);
1225 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001226 }
1227
Chris Dalton6ce447a2019-06-23 18:07:38 -06001228 GrProcessorSet::Analysis finalize(
1229 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1230 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001231 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001232 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001233 GrProcessorAnalysisCoverage::kSingleChannel, color,
1234 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001235 }
1236
1237 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1238
bsalomone46f9fe2015-08-18 06:05:14 -07001239private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001240 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001241
Robert Phillips4133dc42020-03-11 15:55:55 -04001242 void onCreateProgramInfo(const GrCaps* caps,
1243 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001244 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001245 GrAppliedClip&& appliedClip,
Greg Danield358cbe2020-09-11 09:33:54 -04001246 const GrXferProcessor::DstProxyView& dstProxyView,
1247 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001248 SkMatrix localMatrix;
1249 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001250 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001251 }
1252
Robert Phillips4490d922020-03-03 14:50:59 -05001253 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001254 fClipPlaneIsect, fClipPlaneUnion,
1255 fRoundCaps, fWideColor,
1256 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001257
Brian Salomon8afde5f2020-04-01 16:22:00 -04001258 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001259 dstProxyView, gp, GrPrimitiveType::kTriangles,
1260 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001261 }
1262
Robert Phillips4490d922020-03-03 14:50:59 -05001263 void onPrepareDraws(Target* target) override {
1264 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001265 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001266 if (!fProgramInfo) {
1267 return;
1268 }
1269 }
1270
Brian Salomon12d22642019-01-29 14:38:50 -05001271 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001272 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001273 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1274 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001275 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001276 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001277 return;
1278 }
1279
Brian Salomon12d22642019-01-29 14:38:50 -05001280 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001281 int firstIndex = 0;
1282 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1283 if (!indices) {
1284 SkDebugf("Could not allocate indices\n");
1285 return;
1286 }
1287
1288 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001289 for (const auto& circle : fCircles) {
1290 SkScalar innerRadius = circle.fInnerRadius;
1291 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001292 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001293 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001294
joshualitt76e7fb62015-02-11 08:52:27 -08001295 // The inner radius in the vertex data must be specified in normalized space.
1296 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001297 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001298
1299 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001300 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001301
Brian Osman9a24fee2018-08-03 09:48:42 -04001302 SkVector geoClipPlane = { 0, 0 };
1303 SkScalar offsetClipDist = SK_Scalar1;
1304 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1305 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1306 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1307 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1308 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1309 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1310 // the AA can extend just past the center of the circle.
1311 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1312 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1313 SkAssertResult(geoClipPlane.normalize());
1314 offsetClipDist = 0.5f / halfWidth;
1315 }
1316
Brian Osman7d8f82b2018-11-08 10:24:09 -05001317 for (int i = 0; i < 8; ++i) {
1318 // This clips the normalized offset to the half-plane we computed above. Then we
1319 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001320 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001321 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001322 vertices.write(center + offset * halfWidth,
1323 color,
1324 offset,
1325 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001326 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001327 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001328 }
1329 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001330 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001331 }
1332 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001333 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001334 }
1335 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001336 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001337 }
Brian Salomon45c92202018-04-10 10:53:58 -04001338 }
jvanverth6ca48822016-10-07 06:57:32 -07001339
Brian Salomon05441c42017-05-15 16:45:49 -04001340 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001341 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001342
Brian Osman7d8f82b2018-11-08 10:24:09 -05001343 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001344 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1345 color,
1346 kOctagonInner[i] * innerRadius,
1347 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001348 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
1354 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
1357 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001358 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 }
Brian Salomon45c92202018-04-10 10:53:58 -04001360 }
jvanverth6ca48822016-10-07 06:57:32 -07001361 } else {
1362 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001364 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001366 }
jvanverth6ca48822016-10-07 06:57:32 -07001367 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001369 }
1370 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001372 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001373 if (fRoundCaps) {
1374 vertices.write(circle.fRoundCapCenters);
1375 }
jvanverth6ca48822016-10-07 06:57:32 -07001376 }
1377
Brian Salomon05441c42017-05-15 16:45:49 -04001378 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1379 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001380 for (int i = 0; i < primIndexCount; ++i) {
1381 *indices++ = primIndices[i] + currStartVertex;
1382 }
1383
Brian Salomon05441c42017-05-15 16:45:49 -04001384 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001385 }
jvanverth6ca48822016-10-07 06:57:32 -07001386
Robert Phillips4490d922020-03-03 14:50:59 -05001387 fMesh = target->allocMesh();
1388 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001389 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001390 }
1391
1392 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001393 if (!fProgramInfo || !fMesh) {
1394 return;
1395 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001396
Chris Dalton765ed362020-03-16 17:34:44 -06001397 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1398 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1399 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001400 }
1401
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001402 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1403 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,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001607 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,
1610 GrXferBarrierFlags renderPassXferBarriers) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001611 SkMatrix localMatrix;
1612 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001613 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001614 }
1615
1616 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001617 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001618 fWideColor,
1619 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001620
Brian Salomon8afde5f2020-04-01 16:22:00 -04001621 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001622 dstProxyView, gp, GrPrimitiveType::kTriangles,
1623 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001624 }
1625
Robert Phillips4490d922020-03-03 14:50:59 -05001626 void onPrepareDraws(Target* target) override {
1627 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001628 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001629 if (!fProgramInfo) {
1630 return;
1631 }
1632 }
1633
Brian Salomon12d22642019-01-29 14:38:50 -05001634 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001635 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001636 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1637 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001638 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001639 SkDebugf("Could not allocate vertices\n");
1640 return;
1641 }
1642
Brian Salomon12d22642019-01-29 14:38:50 -05001643 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001644 int firstIndex = 0;
1645 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1646 if (!indices) {
1647 SkDebugf("Could not allocate indices\n");
1648 return;
1649 }
1650
1651 int currStartVertex = 0;
1652 for (const auto& circle : fCircles) {
1653 // The inner radius in the vertex data must be specified in normalized space so that
1654 // length() can be called with smaller values to avoid precision issues with half
1655 // floats.
1656 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1657 const SkRect& bounds = circle.fDevBounds;
1658 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001659 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1660 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1661 };
1662 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001663 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001664 dashParams.totalAngle = -dashParams.totalAngle;
1665 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001666 }
1667
Brian Osmane3caf2d2018-11-21 13:48:36 -05001668 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001669
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001670 // The bounding geometry for the circle is composed of an outer bounding octagon and
1671 // an inner bounded octagon.
1672
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001673 // Compute the vertices of the outer octagon.
1674 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1675 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001676
1677 auto reflectY = [=](const SkPoint& p) {
1678 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001679 };
Brian Osman9d958b52018-11-13 12:46:56 -05001680
1681 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001682 vertices.write(center + kOctagonOuter[i] * halfWidth,
1683 color,
1684 reflectY(kOctagonOuter[i]),
1685 circle.fOuterRadius,
1686 normInnerRadius,
1687 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001688 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001689
1690 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001691 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001692 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1693 color,
1694 reflectY(kOctagonInner[i]) * normInnerRadius,
1695 circle.fOuterRadius,
1696 normInnerRadius,
1697 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001698 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001699
1700 const uint16_t* primIndices = circle_type_to_indices(true);
1701 const int primIndexCount = circle_type_to_index_count(true);
1702 for (int i = 0; i < primIndexCount; ++i) {
1703 *indices++ = primIndices[i] + currStartVertex;
1704 }
1705
1706 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001707 }
1708
Robert Phillips4490d922020-03-03 14:50:59 -05001709 fMesh = target->allocMesh();
1710 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001711 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001712 }
1713
1714 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001715 if (!fProgramInfo || !fMesh) {
1716 return;
1717 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001718
Chris Dalton765ed362020-03-16 17:34:44 -06001719 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1720 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1721 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001722 }
1723
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001724 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1725 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,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001941 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,
1944 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001945 SkMatrix localMatrix;
1946 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001947 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001948 }
1949
Robert Phillips4490d922020-03-03 14:50:59 -05001950 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1951 fUseScale, localMatrix);
1952
Brian Salomon8afde5f2020-04-01 16:22:00 -04001953 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04001954 dstProxyView, gp, GrPrimitiveType::kTriangles,
1955 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05001956 }
1957
Robert Phillips4490d922020-03-03 14:50:59 -05001958 void onPrepareDraws(Target* target) override {
1959 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001960 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001961 if (!fProgramInfo) {
1962 return;
1963 }
1964 }
1965
1966 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001967 GrVertexWriter verts{helper.vertices()};
1968 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001969 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001970 return;
1971 }
1972
Brian Salomon05441c42017-05-15 16:45:49 -04001973 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001974 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001975 SkScalar xRadius = ellipse.fXRadius;
1976 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001977
1978 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001979 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1980 SkScalarInvert(xRadius),
1981 SkScalarInvert(yRadius),
1982 SkScalarInvert(ellipse.fInnerXRadius),
1983 SkScalarInvert(ellipse.fInnerYRadius)
1984 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001985 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1986 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001987
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001988 if (!fStroked) {
1989 // For filled ellipses we map a unit circle in the vertex attributes rather than
1990 // computing an ellipse and modifying that distance, so we normalize to 1
1991 xMaxOffset /= xRadius;
1992 yMaxOffset /= yRadius;
1993 }
1994
joshualitt76e7fb62015-02-11 08:52:27 -08001995 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001996 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1997 color,
1998 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05001999 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002000 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002001 }
Robert Phillips4490d922020-03-03 14:50:59 -05002002 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002003 }
2004
2005 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002006 if (!fProgramInfo || !fMesh) {
2007 return;
2008 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002009
Chris Dalton765ed362020-03-16 17:34:44 -06002010 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2011 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2012 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002013 }
2014
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002015 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2016 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,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002218 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,
2221 GrXferBarrierFlags renderPassXferBarriers) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002222 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2223 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002224 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002225
Brian Salomon8afde5f2020-04-01 16:22:00 -04002226 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002227 dstProxyView, gp, GrPrimitiveType::kTriangles,
2228 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002229 }
2230
Robert Phillips4490d922020-03-03 14:50:59 -05002231 void onPrepareDraws(Target* target) override {
2232 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002233 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002234 }
2235
2236 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002237 GrVertexWriter verts{helper.vertices()};
2238 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002239 return;
2240 }
2241
Brian Salomon05441c42017-05-15 16:45:49 -04002242 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002243 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002244 SkScalar xRadius = ellipse.fXRadius;
2245 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002246
joshualitt76e7fb62015-02-11 08:52:27 -08002247 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002248 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2249 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002250
Brian Osman9d958b52018-11-13 12:46:56 -05002251 // By default, constructed so that inner offset is (0, 0) for all points
2252 SkScalar innerRatioX = -offsetDx;
2253 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002254
Brian Osman9d958b52018-11-13 12:46:56 -05002255 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002256 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002257 innerRatioX = xRadius / ellipse.fInnerXRadius;
2258 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002259 }
joshualitt76e7fb62015-02-11 08:52:27 -08002260
Brian Osman2b6e3902018-11-21 15:29:43 -05002261 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2262 color,
2263 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002264 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002265 origin_centered_tri_strip(innerRatioX + offsetDx,
2266 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002267 }
Robert Phillips4490d922020-03-03 14:50:59 -05002268 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002269 }
2270
2271 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002272 if (!fProgramInfo || !fMesh) {
2273 return;
2274 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002275
Chris Dalton765ed362020-03-16 17:34:44 -06002276 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2277 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2278 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002279 }
halcanary9d524f22016-03-29 09:03:52 -07002280
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002281 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2282 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,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002617 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,
2620 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002621 // Invert the view matrix as a local matrix (if any other processors require coords).
2622 SkMatrix localMatrix;
2623 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002624 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002625 }
2626
Robert Phillips4490d922020-03-03 14:50:59 -05002627 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002628 false, false, false, false,
2629 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002630
Brian Salomon8afde5f2020-04-01 16:22:00 -04002631 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002632 dstProxyView, gp, GrPrimitiveType::kTriangles,
2633 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002634 }
2635
Robert Phillips4490d922020-03-03 14:50:59 -05002636 void onPrepareDraws(Target* target) override {
2637 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002638 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002639 if (!fProgramInfo) {
2640 return;
2641 }
2642 }
2643
Brian Salomon12d22642019-01-29 14:38:50 -05002644 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002645 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002646
Robert Phillips4490d922020-03-03 14:50:59 -05002647 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2648 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002649 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002650 SkDebugf("Could not allocate vertices\n");
2651 return;
2652 }
2653
Brian Salomon12d22642019-01-29 14:38:50 -05002654 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002655 int firstIndex = 0;
2656 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2657 if (!indices) {
2658 SkDebugf("Could not allocate indices\n");
2659 return;
2660 }
2661
2662 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002663 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002664 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002665 SkScalar outerRadius = rrect.fOuterRadius;
2666 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002667
Brian Salomon289e3d82016-12-14 15:52:56 -05002668 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2669 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002670
Brian Salomon289e3d82016-12-14 15:52:56 -05002671 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002672 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002673 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002674 SkScalar innerRadius = rrect.fType != kFill_RRectType
2675 ? rrect.fInnerRadius / rrect.fOuterRadius
2676 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002677 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002678 verts.write(bounds.fLeft, yCoords[i],
2679 color,
2680 -1.0f, yOuterRadii[i],
2681 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002682
Brian Osmana1d4eb92018-12-06 16:33:10 -05002683 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2684 color,
2685 0.0f, yOuterRadii[i],
2686 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002687
Brian Osmana1d4eb92018-12-06 16:33:10 -05002688 verts.write(bounds.fRight - outerRadius, yCoords[i],
2689 color,
2690 0.0f, yOuterRadii[i],
2691 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002692
Brian Osmana1d4eb92018-12-06 16:33:10 -05002693 verts.write(bounds.fRight, yCoords[i],
2694 color,
2695 1.0f, yOuterRadii[i],
2696 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002697 }
jvanverthc3d0e422016-08-25 08:12:35 -07002698 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002699 // Effectively this is an additional stroked rrect, with its
2700 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2701 // This will give us correct AA in the center and the correct
2702 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002703 //
jvanvertha4f1af82016-08-29 07:17:47 -07002704 // Also, the outer offset is a constant vector pointing to the right, which
2705 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002706 if (kOverstroke_RRectType == rrect.fType) {
2707 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002708
Brian Salomon05441c42017-05-15 16:45:49 -04002709 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002710 // this is the normalized distance from the outer rectangle of this
2711 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002712 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002713
Brian Osmana1d4eb92018-12-06 16:33:10 -05002714 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002715 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002716 }
jvanverth6a397612016-08-26 08:15:33 -07002717
Brian Salomon05441c42017-05-15 16:45:49 -04002718 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2719 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002720 for (int i = 0; i < primIndexCount; ++i) {
2721 *indices++ = primIndices[i] + currStartVertex;
2722 }
2723
Brian Salomon05441c42017-05-15 16:45:49 -04002724 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002725 }
2726
Robert Phillips4490d922020-03-03 14:50:59 -05002727 fMesh = target->allocMesh();
2728 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002729 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002730 }
2731
2732 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002733 if (!fProgramInfo || !fMesh) {
2734 return;
2735 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002736
Chris Dalton765ed362020-03-16 17:34:44 -06002737 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2738 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2739 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002740 }
2741
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002742 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2743 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,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002941 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,
2944 GrXferBarrierFlags renderPassXferBarriers) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002945 SkMatrix localMatrix;
2946 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002947 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002948 }
2949
Robert Phillips4490d922020-03-03 14:50:59 -05002950 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2951 fUseScale, localMatrix);
2952
Brian Salomon8afde5f2020-04-01 16:22:00 -04002953 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Greg Danield358cbe2020-09-11 09:33:54 -04002954 dstProxyView, gp, GrPrimitiveType::kTriangles,
2955 renderPassXferBarriers);
Robert Phillips4490d922020-03-03 14:50:59 -05002956 }
2957
Robert Phillips4490d922020-03-03 14:50:59 -05002958 void onPrepareDraws(Target* target) override {
2959 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002960 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002961 if (!fProgramInfo) {
2962 return;
2963 }
2964 }
joshualitt76e7fb62015-02-11 08:52:27 -08002965
bsalomonb5238a72015-05-05 07:49:49 -07002966 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002967 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002968 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2969 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002970
Brian Salomon12d22642019-01-29 14:38:50 -05002971 if (!indexBuffer) {
2972 SkDebugf("Could not allocate indices\n");
2973 return;
2974 }
Robert Phillips4490d922020-03-03 14:50:59 -05002975 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2976 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002977 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002978 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002979 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002980 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002981 SkDebugf("Could not allocate vertices\n");
2982 return;
2983 }
2984
Brian Salomon05441c42017-05-15 16:45:49 -04002985 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002986 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002987 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002988 float reciprocalRadii[4] = {
2989 SkScalarInvert(rrect.fXRadius),
2990 SkScalarInvert(rrect.fYRadius),
2991 SkScalarInvert(rrect.fInnerXRadius),
2992 SkScalarInvert(rrect.fInnerYRadius)
2993 };
joshualitt76e7fb62015-02-11 08:52:27 -08002994
2995 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00002996 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2997 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002998
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002999 SkScalar xMaxOffset = xOuterRadius;
3000 SkScalar yMaxOffset = yOuterRadius;
3001 if (!fStroked) {
3002 // For filled rrects we map a unit circle in the vertex attributes rather than
3003 // computing an ellipse and modifying that distance, so we normalize to 1.
3004 xMaxOffset /= rrect.fXRadius;
3005 yMaxOffset /= rrect.fYRadius;
3006 }
3007
Brian Salomon05441c42017-05-15 16:45:49 -04003008 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003009
Brian Salomon289e3d82016-12-14 15:52:56 -05003010 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3011 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003012 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003013 SK_ScalarNearlyZero, // we're using inversesqrt() in
3014 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003015 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003016
Brian Osman788b9162020-02-07 10:36:46 -05003017 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003018 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003019 verts.write(bounds.fLeft, yCoords[i],
3020 color,
3021 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003022 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003023 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003024
Brian Osmana1d4eb92018-12-06 16:33:10 -05003025 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3026 color,
3027 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003028 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003029 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003030
Brian Osmana1d4eb92018-12-06 16:33:10 -05003031 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3032 color,
3033 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003034 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003035 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003036
Brian Osmana1d4eb92018-12-06 16:33:10 -05003037 verts.write(bounds.fRight, yCoords[i],
3038 color,
3039 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003040 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003041 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003042 }
3043 }
Robert Phillips4490d922020-03-03 14:50:59 -05003044 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003045 }
3046
3047 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003048 if (!fProgramInfo || !fMesh) {
3049 return;
3050 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003051
Chris Dalton765ed362020-03-16 17:34:44 -06003052 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3053 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3054 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003055 }
3056
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003057 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3058 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003059 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003060
Brian Salomon05441c42017-05-15 16:45:49 -04003061 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003062 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003063 }
3064
bsalomoncdaa97b2016-03-08 08:30:14 -08003065 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003066 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003067 }
3068
Brian Salomon05441c42017-05-15 16:45:49 -04003069 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003070 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3071 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003072 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003073 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003074
Brian Salomon05441c42017-05-15 16:45:49 -04003075 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003076 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003077 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003078 }
3079
John Stilesaf366522020-08-13 09:57:34 -04003080#if GR_TEST_UTILS
3081 SkString onDumpInfo() const override {
3082 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3083 for (const auto& geo : fRRects) {
3084 string.appendf(
3085 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3086 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3087 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3088 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3089 geo.fInnerXRadius, geo.fInnerYRadius);
3090 }
3091 string += fHelper.dumpInfo();
3092 return string;
3093 }
3094#endif
3095
Brian Salomon05441c42017-05-15 16:45:49 -04003096 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003097 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003098 SkScalar fXRadius;
3099 SkScalar fYRadius;
3100 SkScalar fInnerXRadius;
3101 SkScalar fInnerYRadius;
3102 SkRect fDevBounds;
3103 };
3104
Brian Salomon289e3d82016-12-14 15:52:56 -05003105 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003106 Helper fHelper;
3107 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003108 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003109 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003110 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003111
Chris Daltoneb694b72020-03-16 09:25:50 -06003112 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003113 GrProgramInfo* fProgramInfo = nullptr;
3114
John Stiles7571f9e2020-09-02 22:42:33 -04003115 using INHERITED = GrMeshDrawOp;
joshualitt76e7fb62015-02-11 08:52:27 -08003116};
3117
Herb Derbyc76d4092020-10-07 16:46:15 -04003118GrOp::Owner GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3119 GrPaint&& paint,
3120 const SkMatrix& viewMatrix,
3121 const SkRRect& rrect,
3122 const SkStrokeRec& stroke,
3123 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003124 SkASSERT(viewMatrix.rectStaysRect());
3125 SkASSERT(viewMatrix.isSimilarity());
3126 SkASSERT(rrect.isSimple());
3127 SkASSERT(!rrect.isOval());
3128 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3129
3130 // RRect ops only handle simple, but not too simple, rrects.
3131 // Do any matrix crunching before we reset the draw state for device coords.
3132 const SkRect& rrectBounds = rrect.getBounds();
3133 SkRect bounds;
3134 viewMatrix.mapRect(&bounds, rrectBounds);
3135
3136 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3137 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3138 viewMatrix[SkMatrix::kMSkewY]));
3139
3140 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3141 SkScalar scaledStroke = -1;
3142 SkScalar strokeWidth = stroke.getWidth();
3143 SkStrokeRec::Style style = stroke.getStyle();
3144
3145 bool isStrokeOnly =
3146 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3147 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3148
3149 if (hasStroke) {
3150 if (SkStrokeRec::kHairline_Style == style) {
3151 scaledStroke = SK_Scalar1;
3152 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003153 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3154 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003155 }
3156 }
3157
3158 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3159 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3160 // patch will have fractional coverage. This only matters when the interior is actually filled.
3161 // We could consider falling back to rect rendering here, since a tiny radius is
3162 // indistinguishable from a square corner.
3163 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3164 return nullptr;
3165 }
3166
3167 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3168 scaledStroke, isStrokeOnly);
3169}
3170
Herb Derbyc76d4092020-10-07 16:46:15 -04003171GrOp::Owner make_rrect_op(GrRecordingContext* context,
3172 GrPaint&& paint,
3173 const SkMatrix& viewMatrix,
3174 const SkRRect& rrect,
3175 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003176 SkASSERT(viewMatrix.rectStaysRect());
3177 SkASSERT(rrect.isSimple());
3178 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003179
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003180 // RRect ops only handle simple, but not too simple, rrects.
3181 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003182 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003183 SkRect bounds;
3184 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003185
Mike Reed242135a2018-02-22 13:41:39 -05003186 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003187 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3188 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3189 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3190 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003191
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003192 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003193
bsalomon4b4a7cc2016-07-08 04:42:54 -07003194 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3195 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003196 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003197
Brian Salomon289e3d82016-12-14 15:52:56 -05003198 bool isStrokeOnly =
3199 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003200 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3201
3202 if (hasStroke) {
3203 if (SkStrokeRec::kHairline_Style == style) {
3204 scaledStroke.set(1, 1);
3205 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003206 scaledStroke.fX = SkScalarAbs(
3207 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3208 scaledStroke.fY = SkScalarAbs(
3209 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003210 }
3211
Jim Van Verth64b85892019-06-17 12:01:46 -04003212 // if half of strokewidth is greater than radius, we don't handle that right now
3213 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3214 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003215 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003216 }
3217 }
3218
Brian Salomon8a97f562019-04-18 14:07:27 -04003219 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003220 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003221 std::swap(xRadius, yRadius);
3222 std::swap(scaledStroke.fX, scaledStroke.fY);
3223 }
3224
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003225 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3226 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3227 // patch will have fractional coverage. This only matters when the interior is actually filled.
3228 // We could consider falling back to rect rendering here, since a tiny radius is
3229 // indistinguishable from a square corner.
3230 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003231 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003232 }
3233
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003234 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003235 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3236 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003237}
3238
Herb Derbyc76d4092020-10-07 16:46:15 -04003239GrOp::Owner GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
3240 GrPaint&& paint,
3241 const SkMatrix& viewMatrix,
3242 const SkRRect& rrect,
3243 const SkStrokeRec& stroke,
3244 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003245 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003246 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003247 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003248 }
3249
3250 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003251 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003252 }
3253
Greg Daniel2655ede2019-04-10 00:49:28 +00003254 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003255}
joshualitt3e708c52015-04-30 13:49:27 -07003256
bsalomon4b4a7cc2016-07-08 04:42:54 -07003257///////////////////////////////////////////////////////////////////////////////
3258
Herb Derbyc76d4092020-10-07 16:46:15 -04003259GrOp::Owner GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3260 GrPaint&& paint,
3261 const SkMatrix& viewMatrix,
3262 const SkRect& oval,
3263 const GrStyle& style,
3264 const GrShaderCaps* shaderCaps) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003265 SkScalar width = oval.width();
3266 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3267 circle_stays_circle(viewMatrix));
3268
3269 auto r = width / 2.f;
3270 SkPoint center = { oval.centerX(), oval.centerY() };
3271 if (style.hasNonDashPathEffect()) {
3272 return nullptr;
3273 } else if (style.isDashed()) {
3274 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3275 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3276 return nullptr;
3277 }
3278 auto onInterval = style.dashIntervals()[0];
3279 auto offInterval = style.dashIntervals()[1];
3280 if (offInterval == 0) {
3281 GrStyle strokeStyle(style.strokeRec(), nullptr);
3282 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3283 strokeStyle, shaderCaps);
3284 } else if (onInterval == 0) {
3285 // There is nothing to draw but we have no way to indicate that here.
3286 return nullptr;
3287 }
3288 auto angularOnInterval = onInterval / r;
3289 auto angularOffInterval = offInterval / r;
3290 auto phaseAngle = style.dashPhase() / r;
3291 // Currently this function doesn't accept ovals with different start angles, though
3292 // it could.
3293 static const SkScalar kStartAngle = 0.f;
3294 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3295 style.strokeRec().getWidth(), kStartAngle,
3296 angularOnInterval, angularOffInterval, phaseAngle);
3297 }
3298 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3299}
3300
Herb Derbyc76d4092020-10-07 16:46:15 -04003301GrOp::Owner GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
3302 GrPaint&& paint,
3303 const SkMatrix& viewMatrix,
3304 const SkRect& oval,
3305 const GrStyle& style,
3306 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003307 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003308 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003309 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3310 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003311 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003312 }
3313
3314 if (style.pathEffect()) {
3315 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003316 }
3317
Stan Ilieveb868aa2017-02-21 11:06:16 -05003318 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003319 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003320 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003321 }
3322
Stan Ilieveb868aa2017-02-21 11:06:16 -05003323 // Otherwise, if we have shader derivative support, render as device-independent
3324 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003325 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3326 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3327 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3328 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3329 // Check for near-degenerate matrix
3330 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003331 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003332 style.strokeRec());
3333 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003334 }
3335
bsalomon4b4a7cc2016-07-08 04:42:54 -07003336 return nullptr;
3337}
3338
3339///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003340
Herb Derbyc76d4092020-10-07 16:46:15 -04003341GrOp::Owner GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
3342 GrPaint&& paint,
3343 const SkMatrix& viewMatrix,
3344 const SkRect& oval, SkScalar startAngle,
3345 SkScalar sweepAngle, bool useCenter,
3346 const GrStyle& style,
3347 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003348 SkASSERT(!oval.isEmpty());
3349 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003350 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003351 if (SkScalarAbs(sweepAngle) >= 360.f) {
3352 return nullptr;
3353 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003354 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3355 return nullptr;
3356 }
3357 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003358 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3359 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003360 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003361 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003362}
3363
3364///////////////////////////////////////////////////////////////////////////////
3365
Hal Canary6f6961e2017-01-31 13:50:44 -05003366#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003367
Brian Salomon05441c42017-05-15 16:45:49 -04003368GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003369 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003370 SkScalar rotate = random->nextSScalar1() * 360.f;
3371 SkScalar translateX = random->nextSScalar1() * 1000.f;
3372 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003373 SkScalar scale;
3374 do {
3375 scale = random->nextSScalar1() * 100.f;
3376 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003377 SkMatrix viewMatrix;
3378 viewMatrix.setRotate(rotate);
3379 viewMatrix.postTranslate(translateX, translateY);
3380 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003381 SkRect circle = GrTest::TestSquare(random);
3382 SkPoint center = {circle.centerX(), circle.centerY()};
3383 SkScalar radius = circle.width() / 2.f;
3384 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003385 CircleOp::ArcParams arcParamsTmp;
3386 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003387 if (random->nextBool()) {
3388 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003389 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3390 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003391 arcParams = &arcParamsTmp;
3392 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003393 GrOp::Owner op = CircleOp::Make(context, std::move(paint), viewMatrix,
3394 center, radius,
3395 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003396 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003397 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003398 }
Mike Klein16885072018-12-11 09:54:31 -05003399 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003400 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003401}
3402
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003403GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3404 SkScalar rotate = random->nextSScalar1() * 360.f;
3405 SkScalar translateX = random->nextSScalar1() * 1000.f;
3406 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003407 SkScalar scale;
3408 do {
3409 scale = random->nextSScalar1() * 100.f;
3410 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003411 SkMatrix viewMatrix;
3412 viewMatrix.setRotate(rotate);
3413 viewMatrix.postTranslate(translateX, translateY);
3414 viewMatrix.postScale(scale, scale);
3415 SkRect circle = GrTest::TestSquare(random);
3416 SkPoint center = {circle.centerX(), circle.centerY()};
3417 SkScalar radius = circle.width() / 2.f;
3418 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3419 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3420 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3421 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3422 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003423 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3424 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003425 startAngle, onAngle, offAngle, phase);
3426}
3427
Brian Salomon05441c42017-05-15 16:45:49 -04003428GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003429 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003430 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003431 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003432 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003433}
3434
Brian Salomon05441c42017-05-15 16:45:49 -04003435GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003436 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003437 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003438 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003439 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003440}
3441
Jim Van Verth64b85892019-06-17 12:01:46 -04003442GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3443 do {
3444 SkScalar rotate = random->nextSScalar1() * 360.f;
3445 SkScalar translateX = random->nextSScalar1() * 1000.f;
3446 SkScalar translateY = random->nextSScalar1() * 1000.f;
3447 SkScalar scale;
3448 do {
3449 scale = random->nextSScalar1() * 100.f;
3450 } while (scale == 0);
3451 SkMatrix viewMatrix;
3452 viewMatrix.setRotate(rotate);
3453 viewMatrix.postTranslate(translateX, translateY);
3454 viewMatrix.postScale(scale, scale);
3455 SkRect rect = GrTest::TestRect(random);
3456 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3457 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3458 if (rrect.isOval()) {
3459 continue;
3460 }
Herb Derbyc76d4092020-10-07 16:46:15 -04003461 GrOp::Owner op =
Jim Van Verth64b85892019-06-17 12:01:46 -04003462 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3463 GrTest::TestStrokeRec(random), nullptr);
3464 if (op) {
3465 return op;
3466 }
3467 assert_alive(paint);
3468 } while (true);
3469}
3470
Brian Salomon05441c42017-05-15 16:45:49 -04003471GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003472 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003473 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003474 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003475 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003476}
3477
3478#endif