blob: 37b93ae4780e5da936e340bdebf4c5f852800bc0 [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:
egdaniele659a582015-11-13 09:55:43 -0800227 typedef GrGLSLGeometryProcessor INHERITED;
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
joshualitt249af152014-09-15 11:41:13 -0700247 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248};
249
bsalomoncdaa97b2016-03-08 08:30:14 -0800250GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
Hal Canary6f6961e2017-01-31 13:50:44 -0500252#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500253GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400254 bool stroke = d->fRandom->nextBool();
255 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500256 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400257 bool clipPlane = d->fRandom->nextBool();
258 bool isectPlane = d->fRandom->nextBool();
259 bool unionPlane = d->fRandom->nextBool();
260 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500261 return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
262 unionPlane, roundCaps, wideColor, matrix);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263}
Hal Canary6f6961e2017-01-31 13:50:44 -0500264#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400266class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
267public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500268 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
269 const SkMatrix& localMatrix) {
270 return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400271 }
272
273 ~ButtCapDashedCircleGeometryProcessor() override {}
274
275 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
276
277 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
278 GLSLProcessor::GenKey(*this, caps, b);
279 }
280
281 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
282 return new GLSLProcessor();
283 }
284
285private:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500286 friend class ::SkArenaAlloc; // for access to ctor
287
288 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
289 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
290 , fLocalMatrix(localMatrix) {
291 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
292 fInColor = MakeColorAttribute("inColor", wideColor);
293 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
294 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
295 this->setVertexAttributes(&fInPosition, 4);
296 }
297
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400298 class GLSLProcessor : public GrGLSLGeometryProcessor {
299 public:
300 GLSLProcessor() {}
301
302 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
303 const ButtCapDashedCircleGeometryProcessor& bcscgp =
304 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
305 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
306 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
307 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
308 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
309
310 // emit attributes
311 varyingHandler->emitAttributes(bcscgp);
312 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500313 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314
315 fragBuilder->codeAppend("float4 dashParams;");
316 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500317 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400318 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
319 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
320 varyingHandler->addVarying("wrapDashes", &wrapDashes,
321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
322 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
323 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
324 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500325 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400326 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
327 // x = length of on interval, y = length of on + off.
328 // There are two other parameters in dashParams.zw:
329 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
330 // Each interval has a "corresponding" dash which may be shifted partially or
331 // fully out of its interval by the phase. So there may be up to two "visual"
332 // dashes in an interval.
333 // When computing coverage in an interval we look at three dashes. These are the
334 // "corresponding" dashes from the current, previous, and next intervals. Any of these
335 // may be phase shifted into our interval or even when phase=0 they may be within half a
336 // pixel distance of a pixel center in the interval.
337 // When in the first interval we need to check the dash from the last interval. And
338 // similarly when in the last interval we need to check the dash from the first
339 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
340 // We compute the dash begin/end angles in the vertex shader and apply them in the
341 // fragment shader when we detect we're in the first/last interval.
342 vertBuilder->codeAppend(R"(
343 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
344 // to the fragment shader as a varying.
345 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500346 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400347 // We can happen to be perfectly divisible.
348 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500349 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400350 }
351 // Let 'l' be the last interval before reaching 2 pi.
352 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
353 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
354 // interval.
355 half offset = 0;
356 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500357 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400358 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500359 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400360 }
361 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
362 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
363 // min.
364 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
365
366 // Based on the phase determine whether the -1st, 0th, or 1st interval's
367 // "corresponding" dash appears in the 0th interval and is closest to l.
368 offset = 0;
369 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500370 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400371 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500372 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400373 }
374 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
375 wrapDashes.w = wrapDashes.z + dashParams.x;
376 // The start of the dash we're considering may be clipped by the start of the
377 // circle.
378 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
379 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500380 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400381 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
382 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
383 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
384
385 // setup pass through color
386 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500387 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
389
390 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500391 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400392 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
393 bcscgp.fInPosition.asShaderVar(), bcscgp.fLocalMatrix,
394 &fLocalMatrixUniform);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
400 SkString fnName;
401 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
402 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400406 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400407 )",
408 &fnName);
409 fragBuilder->codeAppend(R"(
410 float d = length(circleEdge.xy) * circleEdge.z;
411
412 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500413 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400414 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500415 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400416 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400417 edgeAlpha *= innerAlpha;
418
Ethan Nicholase1f55022019-02-05 17:17:40 -0500419 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400420 angleFromStart = mod(angleFromStart, 6.28318530718);
421 float x = mod(angleFromStart, dashParams.y);
422 // Convert the radial distance from center to pixel into a diameter.
423 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500424 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
425 half(dashParams.w));
426 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
427 half(dashParams.y) + half(dashParams.x) -
428 half(dashParams.w));
429 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
430 half(-dashParams.y) + half(dashParams.x) -
431 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400432 half dashAlpha = 0;
433 )");
434 fragBuilder->codeAppendf(R"(
435 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500436 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400437 currDash.y = min(currDash.y, lastIntervalLength);
438 if (nextDash.x >= lastIntervalLength) {
439 // The next dash is outside the 0..2pi range, throw it away
440 nextDash.xy = half2(1000);
441 } else {
442 // Clip the end of the next dash to the end of the circle
443 nextDash.y = min(nextDash.y, lastIntervalLength);
444 }
445 }
446 )", fnName.c_str(), fnName.c_str());
447 fragBuilder->codeAppendf(R"(
448 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500449 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400450 currDash.x = max(currDash.x, 0);
451 if (prevDash.y <= 0) {
452 // The previous dash is outside the 0..2pi range, throw it away
453 prevDash.xy = half2(1000);
454 } else {
455 // Clip the start previous dash to the start of the circle
456 prevDash.x = max(prevDash.x, 0);
457 }
458 }
459 )", fnName.c_str(), fnName.c_str());
460 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500461 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
462 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
463 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400464 dashAlpha = min(dashAlpha, 1);
465 edgeAlpha *= dashAlpha;
466 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
467 fnName.c_str());
468 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
469 }
470
471 static void GenKey(const GrGeometryProcessor& gp,
472 const GrShaderCaps&,
473 GrProcessorKeyBuilder* b) {
474 const ButtCapDashedCircleGeometryProcessor& bcscgp =
475 gp.cast<ButtCapDashedCircleGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400476 b->add32(ComputeMatrixKey(bcscgp.fLocalMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400477 }
478
Brian Osman609f1592020-07-01 15:14:39 -0400479 void setData(const GrGLSLProgramDataManager& pdman,
480 const GrPrimitiveProcessor& primProc) override {
Michael Ludwig553db622020-06-19 10:47:30 -0400481 this->setTransform(pdman, fLocalMatrixUniform,
482 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix,
483 &fLocalMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400484 }
485
486 private:
487 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400488
489 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
490 UniformHandle fLocalMatrixUniform;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 };
492
493 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500494 Attribute fInPosition;
495 Attribute fInColor;
496 Attribute fInCircleEdge;
497 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400498
499 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
500
501 typedef GrGeometryProcessor INHERITED;
502};
503
504#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500505GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500506 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400507 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500508 return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400509}
510#endif
511
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000512///////////////////////////////////////////////////////////////////////////////
513
514/**
515 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000516 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
517 * in both x and y directions.
518 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000519 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000520 */
521
bsalomoncdaa97b2016-03-08 08:30:14 -0800522class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000523public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500524 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
525 bool useScale, const SkMatrix& localMatrix) {
526 return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
527 }
528
529 ~EllipseGeometryProcessor() override {}
530
531 const char* name() const override { return "EllipseGeometryProcessor"; }
532
533 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
534 GLSLProcessor::GenKey(*this, caps, b);
535 }
536
537 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
538 return new GLSLProcessor();
539 }
540
541private:
542 friend class ::SkArenaAlloc; // for access to ctor
543
Greg Daniel2655ede2019-04-10 00:49:28 +0000544 EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400545 const SkMatrix& localMatrix)
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500546 : INHERITED(kEllipseGeometryProcessor_ClassID)
547 , fLocalMatrix(localMatrix)
548 , fStroke(stroke)
549 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500550 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500551 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400552 if (useScale) {
553 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
554 } else {
555 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
556 }
557 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500558 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000559 }
560
egdaniel57d3b032015-11-13 11:57:27 -0800561 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800563 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000564
Brian Salomon289e3d82016-12-14 15:52:56 -0500565 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800566 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800567 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800568 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800569 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000570
joshualittabb52a12015-01-13 15:02:10 -0800571 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800572 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800573
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400574 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
575 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800576 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800577 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500578 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000579
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400580 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800581 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500582 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800583
Chris Dalton60283612018-02-14 13:38:14 -0700584 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700585 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500586 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800587
joshualittabb52a12015-01-13 15:02:10 -0800588 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500589 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
Michael Ludwig553db622020-06-19 10:47:30 -0400590 this->writeLocalCoord(vertBuilder, uniformHandler, gpArgs,
591 egp.fInPosition.asShaderVar(), egp.fLocalMatrix,
592 &fLocalMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800593
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400594 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
595 // to compute both the edges because we need two separate test equations for
596 // the single offset.
597 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
598 // the distance by the gradient, non-uniformly scaled by the inverse of the
599 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800600
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400601 // On medium precision devices, we scale the denominator of the distance equation
602 // before taking the inverse square root to minimize the chance that we're dividing
603 // by zero, then we scale the result back.
604
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000605 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400606 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400607 if (egp.fStroke) {
608 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
609 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400610 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
611 if (egp.fUseScale) {
612 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
613 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
614 } else {
615 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
616 }
617 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700618
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000619 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400620 if (args.fShaderCaps->floatIs32Bits()) {
621 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
622 } else {
623 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
624 }
625 if (egp.fUseScale) {
626 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
627 ellipseOffsets.fsIn());
628 } else {
629 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
630 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000631 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000632
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000633 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800634 if (egp.fStroke) {
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400635 fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800636 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400637 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400638 if (egp.fUseScale) {
639 fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
640 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
641 } else {
642 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
643 }
644 fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
645 if (!args.fShaderCaps->floatIs32Bits()) {
646 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
647 }
648 if (egp.fUseScale) {
649 fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
650 ellipseOffsets.fsIn());
651 } else {
652 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
653 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000654 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000655 }
656
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400657 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658 }
659
robertphillips46d36f02015-01-18 08:14:14 -0800660 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500661 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700662 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800663 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400664 uint32_t key = egp.fStroke ? 0x1 : 0x0;
665 key |= ComputeMatrixKey(egp.fLocalMatrix) << 1;
joshualittb8c241a2015-05-19 08:23:30 -0700666 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667 }
668
Brian Osman609f1592020-07-01 15:14:39 -0400669 void setData(const GrGLSLProgramDataManager& pdman,
670 const GrPrimitiveProcessor& primProc) override {
bsalomona624bf32016-09-20 09:12:47 -0700671 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400672 this->setTransform(pdman, fLocalMatrixUniform, egp.fLocalMatrix, &fLocalMatrix);
joshualitte3ababe2015-05-15 07:56:07 -0700673 }
674
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000675 private:
egdaniele659a582015-11-13 09:55:43 -0800676 typedef GrGLSLGeometryProcessor INHERITED;
Michael Ludwig553db622020-06-19 10:47:30 -0400677
678 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
679 UniformHandle fLocalMatrixUniform;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000680 };
681
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500682 Attribute fInPosition;
683 Attribute fInColor;
684 Attribute fInEllipseOffset;
685 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400686
joshualitte3ababe2015-05-15 07:56:07 -0700687 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000688 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400689 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000690
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400691 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000692
joshualitt249af152014-09-15 11:41:13 -0700693 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000694};
695
bsalomoncdaa97b2016-03-08 08:30:14 -0800696GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000697
Hal Canary6f6961e2017-01-31 13:50:44 -0500698#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500699GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
700 return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
701 d->fRandom->nextBool(), d->fRandom->nextBool(),
702 GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000703}
Hal Canary6f6961e2017-01-31 13:50:44 -0500704#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000705
706///////////////////////////////////////////////////////////////////////////////
707
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000708/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000709 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000710 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
711 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
712 * using differentials.
713 *
714 * The result is device-independent and can be used with any affine matrix.
715 */
716
bsalomoncdaa97b2016-03-08 08:30:14 -0800717enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000718
bsalomoncdaa97b2016-03-08 08:30:14 -0800719class DIEllipseGeometryProcessor : public GrGeometryProcessor {
720public:
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500721 static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
722 const SkMatrix& viewMatrix, DIEllipseStyle style) {
723 return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
724 }
725
726 ~DIEllipseGeometryProcessor() override {}
727
728 const char* name() const override { return "DIEllipseGeometryProcessor"; }
729
730 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
731 GLSLProcessor::GenKey(*this, caps, b);
732 }
733
734 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
735 return new GLSLProcessor();
736 }
737
738private:
739 friend class ::SkArenaAlloc; // for access to ctor
740
Greg Daniel2655ede2019-04-10 00:49:28 +0000741 DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400742 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400743 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400744 , fViewMatrix(viewMatrix)
745 , fUseScale(useScale)
746 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500747 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500748 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400749 if (useScale) {
750 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
751 kFloat3_GrSLType};
752 } else {
753 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
754 kFloat2_GrSLType};
755 }
756 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500757 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000758 }
759
egdaniel57d3b032015-11-13 11:57:27 -0800760 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000761 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500762 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763
joshualitt465283c2015-09-11 08:19:35 -0700764 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800765 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800766 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800767 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800768 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000769
joshualittabb52a12015-01-13 15:02:10 -0800770 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800771 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800772
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400773 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
774 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800775 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500776 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700777
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400778 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800779 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500780 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800781
Chris Dalton60283612018-02-14 13:38:14 -0700782 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500783 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800784
joshualittabb52a12015-01-13 15:02:10 -0800785 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400786 this->writeOutputPosition(vertBuilder,
787 uniformHandler,
788 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500789 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400790 diegp.fViewMatrix,
791 &fViewMatrixUniform);
Michael Ludwig553db622020-06-19 10:47:30 -0400792 gpArgs->fLocalCoordVar = diegp.fInPosition.asShaderVar();
joshualitt4973d9d2014-11-08 09:24:25 -0800793
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000794 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400795 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
796 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
797 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
798 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500799 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400800 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
801 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500802 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400803 if (diegp.fUseScale) {
804 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
805 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000806
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400807 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000808 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400809 if (args.fShaderCaps->floatIs32Bits()) {
810 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
811 } else {
812 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
813 }
814 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
815 if (diegp.fUseScale) {
816 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
817 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800818 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000819 // can probably do this with one step
Greg Daniel2655ede2019-04-10 00:49:28 +0000820 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
821 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000822 } else {
Greg Daniel2655ede2019-04-10 00:49:28 +0000823 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000824 }
825
826 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800827 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800828 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
829 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400830 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
831 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500832 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400833 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
834 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500835 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400836 if (diegp.fUseScale) {
837 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
838 }
839 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
840 if (!args.fShaderCaps->floatIs32Bits()) {
841 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
842 }
843 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
844 if (diegp.fUseScale) {
845 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
846 }
Greg Daniel2655ede2019-04-10 00:49:28 +0000847 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000848 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000849
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400850 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000851 }
852
robertphillips46d36f02015-01-18 08:14:14 -0800853 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500854 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700855 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800856 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Michael Ludwig553db622020-06-19 10:47:30 -0400857 uint32_t key = static_cast<uint32_t>(diegp.fStyle);
858 key |= ComputeMatrixKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700859 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000860 }
861
Brian Osman609f1592020-07-01 15:14:39 -0400862 void setData(const GrGLSLProgramDataManager& pdman,
863 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800864 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700865
Michael Ludwig553db622020-06-19 10:47:30 -0400866 this->setTransform(pdman, fViewMatrixUniform, diegp.fViewMatrix, &fViewMatrix);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000867 }
868
869 private:
Michael Ludwig553db622020-06-19 10:47:30 -0400870 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700871 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800872
egdaniele659a582015-11-13 09:55:43 -0800873 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000874 };
875
Brian Salomon92be2f72018-06-19 14:33:47 -0400876
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500877 Attribute fInPosition;
878 Attribute fInColor;
879 Attribute fInEllipseOffsets0;
880 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400881
Brian Salomon289e3d82016-12-14 15:52:56 -0500882 SkMatrix fViewMatrix;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400883 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500884 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000885
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400886 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000887
joshualitt249af152014-09-15 11:41:13 -0700888 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000889};
890
bsalomoncdaa97b2016-03-08 08:30:14 -0800891GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000892
Hal Canary6f6961e2017-01-31 13:50:44 -0500893#if GR_TEST_UTILS
Robert Phillips7cd0bfe2019-11-20 16:08:10 -0500894GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
895 return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
896 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
897 (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000898}
Hal Canary6f6961e2017-01-31 13:50:44 -0500899#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000900
901///////////////////////////////////////////////////////////////////////////////
902
jvanverth6ca48822016-10-07 06:57:32 -0700903// We have two possible cases for geometry for a circle:
904
905// In the case of a normal fill, we draw geometry for the circle as an octagon.
906static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500907 // enter the octagon
908 // clang-format off
909 0, 1, 8, 1, 2, 8,
910 2, 3, 8, 3, 4, 8,
911 4, 5, 8, 5, 6, 8,
912 6, 7, 8, 7, 0, 8
913 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700914};
915
916// For stroked circles, we use two nested octagons.
917static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500918 // enter the octagon
919 // clang-format off
920 0, 1, 9, 0, 9, 8,
921 1, 2, 10, 1, 10, 9,
922 2, 3, 11, 2, 11, 10,
923 3, 4, 12, 3, 12, 11,
924 4, 5, 13, 4, 13, 12,
925 5, 6, 14, 5, 14, 13,
926 6, 7, 15, 6, 15, 14,
927 7, 0, 8, 7, 8, 15,
928 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700929};
930
Brian Osman9d958b52018-11-13 12:46:56 -0500931// Normalized geometry for octagons that circumscribe and lie on a circle:
932
933static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
934static constexpr SkPoint kOctagonOuter[] = {
935 SkPoint::Make(-kOctOffset, -1),
936 SkPoint::Make( kOctOffset, -1),
937 SkPoint::Make( 1, -kOctOffset),
938 SkPoint::Make( 1, kOctOffset),
939 SkPoint::Make( kOctOffset, 1),
940 SkPoint::Make(-kOctOffset, 1),
941 SkPoint::Make(-1, kOctOffset),
942 SkPoint::Make(-1, -kOctOffset),
943};
944
945// cosine and sine of pi/8
946static constexpr SkScalar kCosPi8 = 0.923579533f;
947static constexpr SkScalar kSinPi8 = 0.382683432f;
948static constexpr SkPoint kOctagonInner[] = {
949 SkPoint::Make(-kSinPi8, -kCosPi8),
950 SkPoint::Make( kSinPi8, -kCosPi8),
951 SkPoint::Make( kCosPi8, -kSinPi8),
952 SkPoint::Make( kCosPi8, kSinPi8),
953 SkPoint::Make( kSinPi8, kCosPi8),
954 SkPoint::Make(-kSinPi8, kCosPi8),
955 SkPoint::Make(-kCosPi8, kSinPi8),
956 SkPoint::Make(-kCosPi8, -kSinPi8),
957};
Brian Salomon289e3d82016-12-14 15:52:56 -0500958
jvanverth6ca48822016-10-07 06:57:32 -0700959static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
960static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
961static const int kVertsPerStrokeCircle = 16;
962static const int kVertsPerFillCircle = 9;
963
964static int circle_type_to_vert_count(bool stroked) {
965 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
966}
967
968static int circle_type_to_index_count(bool stroked) {
969 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
970}
971
972static const uint16_t* circle_type_to_indices(bool stroked) {
973 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
974}
975
976///////////////////////////////////////////////////////////////////////////////
977
Brian Salomon05441c42017-05-15 16:45:49 -0400978class CircleOp final : public GrMeshDrawOp {
979private:
980 using Helper = GrSimpleMeshDrawOpHelper;
981
joshualitt76e7fb62015-02-11 08:52:27 -0800982public:
Brian Salomon25a88092016-12-01 09:36:50 -0500983 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700984
bsalomon4f3a0ca2016-08-22 13:14:26 -0700985 /** Optional extra params to render a partial arc rather than a full circle. */
986 struct ArcParams {
987 SkScalar fStartAngleRadians;
988 SkScalar fSweepAngleRadians;
989 bool fUseCenter;
990 };
Brian Salomon05441c42017-05-15 16:45:49 -0400991
Robert Phillipsb97da532019-02-12 15:24:12 -0500992 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -0400993 GrPaint&& paint,
994 const SkMatrix& viewMatrix,
995 SkPoint center,
996 SkScalar radius,
997 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -0400998 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700999 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001000 if (style.hasPathEffect()) {
1001 return nullptr;
1002 }
Brian Salomon05441c42017-05-15 16:45:49 -04001003 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001004 SkStrokeRec::Style recStyle = stroke.getStyle();
1005 if (arcParams) {
1006 // Arc support depends on the style.
1007 switch (recStyle) {
1008 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001009 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001010 return nullptr;
1011 case SkStrokeRec::kFill_Style:
1012 // This supports all fills.
1013 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001014 case SkStrokeRec::kStroke_Style:
1015 // Strokes that don't use the center point are supported with butt and round
1016 // caps.
1017 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1018 return nullptr;
1019 }
1020 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001021 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001022 // Hairline only supports butt cap. Round caps could be emulated by slightly
1023 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001024 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1025 return nullptr;
1026 }
1027 break;
1028 }
1029 }
Greg Daniel2655ede2019-04-10 00:49:28 +00001030 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
1031 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001032 }
1033
Greg Daniel2655ede2019-04-10 00:49:28 +00001034 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osmancf860852018-10-31 14:04:39 -04001035 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1036 const ArcParams* arcParams)
Robert Phillips4133dc42020-03-11 15:55:55 -04001037 : GrMeshDrawOp(ClassID())
1038 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001039 const SkStrokeRec& stroke = style.strokeRec();
1040 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001041
Brian Salomon45c92202018-04-10 10:53:58 -04001042 fRoundCaps = false;
Greg Daniel2655ede2019-04-10 00:49:28 +00001043
bsalomon4b4a7cc2016-07-08 04:42:54 -07001044 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001045 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001046 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001047
Brian Salomon289e3d82016-12-14 15:52:56 -05001048 bool isStrokeOnly =
1049 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001050 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001051
jvanverth6ca48822016-10-07 06:57:32 -07001052 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001053 SkScalar outerRadius = radius;
1054 SkScalar halfWidth = 0;
1055 if (hasStroke) {
1056 if (SkScalarNearlyZero(strokeWidth)) {
1057 halfWidth = SK_ScalarHalf;
1058 } else {
1059 halfWidth = SkScalarHalf(strokeWidth);
1060 }
1061
1062 outerRadius += halfWidth;
1063 if (isStrokeOnly) {
1064 innerRadius = radius - halfWidth;
1065 }
1066 }
1067
1068 // The radii are outset for two reasons. First, it allows the shader to simply perform
1069 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1070 // Second, the outer radius is used to compute the verts of the bounding box that is
1071 // rendered and the outset ensures the box will cover all partially covered by the circle.
Greg Daniel2655ede2019-04-10 00:49:28 +00001072 outerRadius += SK_ScalarHalf;
1073 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -07001074 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001075 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001076
bsalomon4f3a0ca2016-08-22 13:14:26 -07001077 // This makes every point fully inside the intersection plane.
1078 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1079 // This makes every point fully outside the union plane.
1080 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001081 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001082 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1083 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001084 if (arcParams) {
1085 // The shader operates in a space where the circle is translated to be centered at the
1086 // origin. Here we compute points on the unit circle at the starting and ending angles.
1087 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001088 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1089 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001090 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001091 stopPoint.fY = SkScalarSin(endAngle);
1092 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001093
1094 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1095 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1096 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1097 startPoint.normalize();
1098 stopPoint.normalize();
1099
Brian Salomon3517aa72019-12-11 08:16:22 -05001100 // We know the matrix is a similarity here. Detect mirroring which will affect how we
1101 // should orient the clip planes for arcs.
1102 SkASSERT(viewMatrix.isSimilarity());
1103 auto upperLeftDet = viewMatrix.getScaleX()*viewMatrix.getScaleY() -
1104 viewMatrix.getSkewX() *viewMatrix.getSkewY();
1105 if (upperLeftDet < 0) {
1106 std::swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001107 }
1108
Brian Salomon45c92202018-04-10 10:53:58 -04001109 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1110 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1111 SkPoint roundCaps[2];
1112 if (fRoundCaps) {
1113 // Compute the cap center points in the normalized space.
1114 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1115 roundCaps[0] = startPoint * midRadius;
1116 roundCaps[1] = stopPoint * midRadius;
1117 } else {
1118 roundCaps[0] = kUnusedRoundCaps[0];
1119 roundCaps[1] = kUnusedRoundCaps[1];
1120 }
1121
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001123 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1124 // center of the butts.
1125 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001127 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001129 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1130 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1131 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001132 if (useCenter) {
1133 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1134 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001135 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1136 if (arcParams->fSweepAngleRadians < 0) {
1137 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001138 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001139 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001140 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001141 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001142 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001143 color,
1144 innerRadius,
1145 outerRadius,
1146 {norm0.fX, norm0.fY, 0.5f},
1147 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1148 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001149 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001150 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001151 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001152 fClipPlaneIsect = false;
1153 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001154 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001155 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001156 color,
1157 innerRadius,
1158 outerRadius,
1159 {norm0.fX, norm0.fY, 0.5f},
1160 {norm1.fX, norm1.fY, 0.5f},
1161 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001162 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001163 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001164 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001165 fClipPlaneIsect = true;
1166 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001167 }
1168 } else {
1169 // We clip to a secant of the original circle.
1170 startPoint.scale(radius);
1171 stopPoint.scale(radius);
1172 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1173 norm.normalize();
1174 if (arcParams->fSweepAngleRadians > 0) {
1175 norm.negate();
1176 }
1177 SkScalar d = -norm.dot(startPoint) + 0.5f;
1178
Brian Salomon05441c42017-05-15 16:45:49 -04001179 fCircles.emplace_back(
1180 Circle{color,
1181 innerRadius,
1182 outerRadius,
1183 {norm.fX, norm.fY, d},
1184 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1185 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001186 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001187 devBounds,
1188 stroked});
1189 fClipPlane = true;
1190 fClipPlaneIsect = false;
1191 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001192 }
1193 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001194 fCircles.emplace_back(
1195 Circle{color,
1196 innerRadius,
1197 outerRadius,
1198 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1199 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1200 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001201 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001202 devBounds,
1203 stroked});
1204 fClipPlane = false;
1205 fClipPlaneIsect = false;
1206 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001207 }
bsalomon88cf17d2016-07-08 06:40:56 -07001208 // Use the original radius and stroke radius for the bounds so that it does not include the
1209 // AA bloat.
1210 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001211 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001212 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001213 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001214 fVertCount = circle_type_to_vert_count(stroked);
1215 fIndexCount = circle_type_to_index_count(stroked);
1216 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001217 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001218
Brian Salomon289e3d82016-12-14 15:52:56 -05001219 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001220
Chris Dalton1706cbf2019-05-21 19:35:29 -06001221 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001222 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001223 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001224 } else {
1225 fHelper.visitProxies(func);
1226 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001227 }
1228
Chris Dalton6ce447a2019-06-23 18:07:38 -06001229 GrProcessorSet::Analysis finalize(
1230 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1231 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001232 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001233 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001234 GrProcessorAnalysisCoverage::kSingleChannel, color,
1235 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001236 }
1237
1238 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1239
bsalomone46f9fe2015-08-18 06:05:14 -07001240private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001241 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001242
Robert Phillips4133dc42020-03-11 15:55:55 -04001243 void onCreateProgramInfo(const GrCaps* caps,
1244 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001245 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001246 GrAppliedClip&& appliedClip,
1247 const GrXferProcessor::DstProxyView& dstProxyView) 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),
Robert Phillips4133dc42020-03-11 15:55:55 -04001259 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001260 }
1261
Robert Phillips4490d922020-03-03 14:50:59 -05001262 void onPrepareDraws(Target* target) override {
1263 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001264 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001265 if (!fProgramInfo) {
1266 return;
1267 }
1268 }
1269
Brian Salomon12d22642019-01-29 14:38:50 -05001270 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001271 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001272 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1273 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001274 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001275 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001276 return;
1277 }
1278
Brian Salomon12d22642019-01-29 14:38:50 -05001279 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001280 int firstIndex = 0;
1281 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1282 if (!indices) {
1283 SkDebugf("Could not allocate indices\n");
1284 return;
1285 }
1286
1287 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001288 for (const auto& circle : fCircles) {
1289 SkScalar innerRadius = circle.fInnerRadius;
1290 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001291 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001292 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001293
joshualitt76e7fb62015-02-11 08:52:27 -08001294 // The inner radius in the vertex data must be specified in normalized space.
1295 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001296 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001297
1298 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001299 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001300
Brian Osman9a24fee2018-08-03 09:48:42 -04001301 SkVector geoClipPlane = { 0, 0 };
1302 SkScalar offsetClipDist = SK_Scalar1;
1303 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1304 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1305 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1306 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1307 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1308 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1309 // the AA can extend just past the center of the circle.
1310 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1311 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1312 SkAssertResult(geoClipPlane.normalize());
1313 offsetClipDist = 0.5f / halfWidth;
1314 }
1315
Brian Osman7d8f82b2018-11-08 10:24:09 -05001316 for (int i = 0; i < 8; ++i) {
1317 // This clips the normalized offset to the half-plane we computed above. Then we
1318 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001319 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001320 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001321 vertices.write(center + offset * halfWidth,
1322 color,
1323 offset,
1324 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001325 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001326 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001327 }
1328 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001329 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001330 }
1331 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001332 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001333 }
1334 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001335 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001336 }
Brian Salomon45c92202018-04-10 10:53:58 -04001337 }
jvanverth6ca48822016-10-07 06:57:32 -07001338
Brian Salomon05441c42017-05-15 16:45:49 -04001339 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001340 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001341
Brian Osman7d8f82b2018-11-08 10:24:09 -05001342 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001343 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1344 color,
1345 kOctagonInner[i] * innerRadius,
1346 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001348 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001349 }
1350 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001351 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001352 }
1353 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001354 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001355 }
1356 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001357 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001358 }
Brian Salomon45c92202018-04-10 10:53:58 -04001359 }
jvanverth6ca48822016-10-07 06:57:32 -07001360 } else {
1361 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001362 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001363 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001364 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001365 }
jvanverth6ca48822016-10-07 06:57:32 -07001366 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001367 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001368 }
1369 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001370 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001371 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001372 if (fRoundCaps) {
1373 vertices.write(circle.fRoundCapCenters);
1374 }
jvanverth6ca48822016-10-07 06:57:32 -07001375 }
1376
Brian Salomon05441c42017-05-15 16:45:49 -04001377 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1378 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001379 for (int i = 0; i < primIndexCount; ++i) {
1380 *indices++ = primIndices[i] + currStartVertex;
1381 }
1382
Brian Salomon05441c42017-05-15 16:45:49 -04001383 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001384 }
jvanverth6ca48822016-10-07 06:57:32 -07001385
Robert Phillips4490d922020-03-03 14:50:59 -05001386 fMesh = target->allocMesh();
1387 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001388 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001389 }
1390
1391 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001392 if (!fProgramInfo || !fMesh) {
1393 return;
1394 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001395
Chris Dalton765ed362020-03-16 17:34:44 -06001396 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1397 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1398 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001399 }
1400
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001401 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1402 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001403 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001404
1405 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001406 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001407 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001408 }
1409
Brian Salomon05441c42017-05-15 16:45:49 -04001410 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001411 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001412 }
1413
Brian Salomon05441c42017-05-15 16:45:49 -04001414 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001415 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1416 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001417 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001418 }
1419
Brian Salomon289e3d82016-12-14 15:52:56 -05001420 // Because we've set up the ops that don't use the planes with noop values
1421 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001422 fClipPlane |= that->fClipPlane;
1423 fClipPlaneIsect |= that->fClipPlaneIsect;
1424 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001425 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001426 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001427
Brian Salomon05441c42017-05-15 16:45:49 -04001428 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001429 fVertCount += that->fVertCount;
1430 fIndexCount += that->fIndexCount;
1431 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001432 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001433 }
1434
John Stilesaf366522020-08-13 09:57:34 -04001435#if GR_TEST_UTILS
1436 SkString onDumpInfo() const override {
1437 SkString string;
1438 for (int i = 0; i < fCircles.count(); ++i) {
1439 string.appendf(
1440 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1441 "InnerRad: %.2f, OuterRad: %.2f\n",
1442 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1443 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1444 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1445 fCircles[i].fOuterRadius);
1446 }
1447 string += fHelper.dumpInfo();
1448 return string;
1449 }
1450#endif
1451
Brian Salomon05441c42017-05-15 16:45:49 -04001452 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001453 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 SkScalar fInnerRadius;
1455 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001456 SkScalar fClipPlane[3];
1457 SkScalar fIsectPlane[3];
1458 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001459 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001460 SkRect fDevBounds;
1461 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462 };
1463
Brian Salomon289e3d82016-12-14 15:52:56 -05001464 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001465 Helper fHelper;
1466 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001467 int fVertCount;
1468 int fIndexCount;
1469 bool fAllFill;
1470 bool fClipPlane;
1471 bool fClipPlaneIsect;
1472 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001473 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001474 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001475
Chris Daltoneb694b72020-03-16 09:25:50 -06001476 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001477 GrProgramInfo* fProgramInfo = nullptr;
1478
Brian Salomon05441c42017-05-15 16:45:49 -04001479 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001480};
1481
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001482class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1483private:
1484 using Helper = GrSimpleMeshDrawOpHelper;
1485
1486public:
1487 DEFINE_OP_CLASS_ID
1488
Robert Phillipsb97da532019-02-12 15:24:12 -05001489 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001490 GrPaint&& paint,
1491 const SkMatrix& viewMatrix,
1492 SkPoint center,
1493 SkScalar radius,
1494 SkScalar strokeWidth,
1495 SkScalar startAngle,
1496 SkScalar onAngle,
1497 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001498 SkScalar phaseAngle) {
1499 SkASSERT(circle_stays_circle(viewMatrix));
1500 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001501 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1502 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001503 onAngle, offAngle, phaseAngle);
1504 }
1505
Brian Osmancf860852018-10-31 14:04:39 -04001506 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001507 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1508 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1509 SkScalar offAngle, SkScalar phaseAngle)
Robert Phillips4133dc42020-03-11 15:55:55 -04001510 : GrMeshDrawOp(ClassID())
1511 , fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001512 SkASSERT(circle_stays_circle(viewMatrix));
1513 viewMatrix.mapPoints(&center, 1);
1514 radius = viewMatrix.mapRadius(radius);
1515 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1516
1517 // Determine the angle where the circle starts in device space and whether its orientation
1518 // has been reversed.
1519 SkVector start;
1520 bool reflection;
1521 if (!startAngle) {
1522 start = {1, 0};
1523 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001524 start.fY = SkScalarSin(startAngle);
1525 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526 }
1527 viewMatrix.mapVectors(&start, 1);
1528 startAngle = SkScalarATan2(start.fY, start.fX);
1529 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1530 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1531
1532 auto totalAngle = onAngle + offAngle;
1533 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1534
1535 SkScalar halfWidth = 0;
1536 if (SkScalarNearlyZero(strokeWidth)) {
1537 halfWidth = SK_ScalarHalf;
1538 } else {
1539 halfWidth = SkScalarHalf(strokeWidth);
1540 }
1541
1542 SkScalar outerRadius = radius + halfWidth;
1543 SkScalar innerRadius = radius - halfWidth;
1544
1545 // The radii are outset for two reasons. First, it allows the shader to simply perform
1546 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1547 // Second, the outer radius is used to compute the verts of the bounding box that is
1548 // rendered and the outset ensures the box will cover all partially covered by the circle.
1549 outerRadius += SK_ScalarHalf;
1550 innerRadius -= SK_ScalarHalf;
1551 fViewMatrixIfUsingLocalCoords = viewMatrix;
1552
1553 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1554 center.fX + outerRadius, center.fY + outerRadius);
1555
1556 // We store whether there is a reflection as a negative total angle.
1557 if (reflection) {
1558 totalAngle = -totalAngle;
1559 }
1560 fCircles.push_back(Circle{
1561 color,
1562 outerRadius,
1563 innerRadius,
1564 onAngle,
1565 totalAngle,
1566 startAngle,
1567 phaseAngle,
1568 devBounds
1569 });
1570 // Use the original radius and stroke radius for the bounds so that it does not include the
1571 // AA bloat.
1572 radius += halfWidth;
1573 this->setBounds(
1574 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Greg Daniel5faf4742019-10-01 15:14:44 -04001575 HasAABloat::kYes, IsHairline::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001576 fVertCount = circle_type_to_vert_count(true);
1577 fIndexCount = circle_type_to_index_count(true);
1578 }
1579
1580 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1581
Chris Dalton1706cbf2019-05-21 19:35:29 -06001582 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001583 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001584 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001585 } else {
1586 fHelper.visitProxies(func);
1587 }
Brian Salomon7d94bb52018-10-12 14:37:19 -04001588 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001589
Chris Dalton6ce447a2019-06-23 18:07:38 -06001590 GrProcessorSet::Analysis finalize(
1591 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1592 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001593 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001594 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001595 GrProcessorAnalysisCoverage::kSingleChannel, color,
1596 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001597 }
1598
1599 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1600
1601private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001602 GrProgramInfo* programInfo() override { return fProgramInfo; }
1603
Robert Phillips4133dc42020-03-11 15:55:55 -04001604 void onCreateProgramInfo(const GrCaps* caps,
1605 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001606 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001607 GrAppliedClip&& appliedClip,
1608 const GrXferProcessor::DstProxyView& dstProxyView) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001609 SkMatrix localMatrix;
1610 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001611 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001612 }
1613
1614 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001615 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001616 fWideColor,
1617 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001618
Brian Salomon8afde5f2020-04-01 16:22:00 -04001619 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001620 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001621 }
1622
Robert Phillips4490d922020-03-03 14:50:59 -05001623 void onPrepareDraws(Target* target) override {
1624 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001625 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001626 if (!fProgramInfo) {
1627 return;
1628 }
1629 }
1630
Brian Salomon12d22642019-01-29 14:38:50 -05001631 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001632 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001633 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1634 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001635 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001636 SkDebugf("Could not allocate vertices\n");
1637 return;
1638 }
1639
Brian Salomon12d22642019-01-29 14:38:50 -05001640 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001641 int firstIndex = 0;
1642 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1643 if (!indices) {
1644 SkDebugf("Could not allocate indices\n");
1645 return;
1646 }
1647
1648 int currStartVertex = 0;
1649 for (const auto& circle : fCircles) {
1650 // The inner radius in the vertex data must be specified in normalized space so that
1651 // length() can be called with smaller values to avoid precision issues with half
1652 // floats.
1653 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1654 const SkRect& bounds = circle.fDevBounds;
1655 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001656 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1657 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1658 };
1659 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001661 dashParams.totalAngle = -dashParams.totalAngle;
1662 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001663 }
1664
Brian Osmane3caf2d2018-11-21 13:48:36 -05001665 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001666
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001667 // The bounding geometry for the circle is composed of an outer bounding octagon and
1668 // an inner bounded octagon.
1669
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001670 // Compute the vertices of the outer octagon.
1671 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1672 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001673
1674 auto reflectY = [=](const SkPoint& p) {
1675 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676 };
Brian Osman9d958b52018-11-13 12:46:56 -05001677
1678 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001679 vertices.write(center + kOctagonOuter[i] * halfWidth,
1680 color,
1681 reflectY(kOctagonOuter[i]),
1682 circle.fOuterRadius,
1683 normInnerRadius,
1684 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001685 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001686
1687 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001688 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001689 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1690 color,
1691 reflectY(kOctagonInner[i]) * normInnerRadius,
1692 circle.fOuterRadius,
1693 normInnerRadius,
1694 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001695 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001696
1697 const uint16_t* primIndices = circle_type_to_indices(true);
1698 const int primIndexCount = circle_type_to_index_count(true);
1699 for (int i = 0; i < primIndexCount; ++i) {
1700 *indices++ = primIndices[i] + currStartVertex;
1701 }
1702
1703 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001704 }
1705
Robert Phillips4490d922020-03-03 14:50:59 -05001706 fMesh = target->allocMesh();
1707 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001708 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001709 }
1710
1711 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001712 if (!fProgramInfo || !fMesh) {
1713 return;
1714 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001715
Chris Dalton765ed362020-03-16 17:34:44 -06001716 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1717 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1718 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001719 }
1720
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001721 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1722 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1724
1725 // can only represent 65535 unique vertices with 16-bit indices
1726 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001727 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001728 }
1729
1730 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001731 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001732 }
1733
1734 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001735 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1736 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001737 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001738 }
1739
1740 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 fVertCount += that->fVertCount;
1742 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001743 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001744 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001745 }
1746
John Stilesaf366522020-08-13 09:57:34 -04001747#if GR_TEST_UTILS
1748 SkString onDumpInfo() const override {
1749 SkString string;
1750 for (int i = 0; i < fCircles.count(); ++i) {
1751 string.appendf(
1752 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1753 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1754 "Phase: %.2f\n",
1755 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
1756 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1757 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1758 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1759 fCircles[i].fPhaseAngle);
1760 }
1761 string += fHelper.dumpInfo();
1762 return string;
1763 }
1764#endif
1765
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001766 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001767 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001768 SkScalar fOuterRadius;
1769 SkScalar fInnerRadius;
1770 SkScalar fOnAngle;
1771 SkScalar fTotalAngle;
1772 SkScalar fStartAngle;
1773 SkScalar fPhaseAngle;
1774 SkRect fDevBounds;
1775 };
1776
1777 SkMatrix fViewMatrixIfUsingLocalCoords;
1778 Helper fHelper;
1779 SkSTArray<1, Circle, true> fCircles;
1780 int fVertCount;
1781 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001782 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001783
Chris Daltoneb694b72020-03-16 09:25:50 -06001784 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05001785 GrProgramInfo* fProgramInfo = nullptr;
1786
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001787 typedef GrMeshDrawOp INHERITED;
1788};
1789
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001790///////////////////////////////////////////////////////////////////////////////
1791
Brian Salomon05441c42017-05-15 16:45:49 -04001792class EllipseOp : public GrMeshDrawOp {
1793private:
1794 using Helper = GrSimpleMeshDrawOpHelper;
1795
1796 struct DeviceSpaceParams {
1797 SkPoint fCenter;
1798 SkScalar fXRadius;
1799 SkScalar fYRadius;
1800 SkScalar fInnerXRadius;
1801 SkScalar fInnerYRadius;
1802 };
1803
joshualitt76e7fb62015-02-11 08:52:27 -08001804public:
Brian Salomon25a88092016-12-01 09:36:50 -05001805 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001806
Robert Phillipsb97da532019-02-12 15:24:12 -05001807 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001808 GrPaint&& paint,
1809 const SkMatrix& viewMatrix,
1810 const SkRect& ellipse,
1811 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001812 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001813 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001814 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1815 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001816 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1817 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001818 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1819 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1820 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1821 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001822
bsalomon4b4a7cc2016-07-08 04:42:54 -07001823 // do (potentially) anisotropic mapping of stroke
1824 SkVector scaledStroke;
1825 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001826 scaledStroke.fX = SkScalarAbs(
1827 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1828 scaledStroke.fY = SkScalarAbs(
1829 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001830
1831 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001832 bool isStrokeOnly =
1833 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001834 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1835
Brian Salomon05441c42017-05-15 16:45:49 -04001836 params.fInnerXRadius = 0;
1837 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001838 if (hasStroke) {
1839 if (SkScalarNearlyZero(scaledStroke.length())) {
1840 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1841 } else {
1842 scaledStroke.scale(SK_ScalarHalf);
1843 }
1844
1845 // we only handle thick strokes for near-circular ellipses
1846 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001847 (0.5f * params.fXRadius > params.fYRadius ||
1848 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001849 return nullptr;
1850 }
1851
1852 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001853 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1854 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1855 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1856 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001857 return nullptr;
1858 }
1859
1860 // this is legit only if scale & translation (which should be the case at the moment)
1861 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001862 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1863 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001864 }
1865
Brian Salomon05441c42017-05-15 16:45:49 -04001866 params.fXRadius += scaledStroke.fX;
1867 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001868 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001869
1870 // For large ovals with low precision floats, we fall back to the path renderer.
1871 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1872 // minimum value to avoid divides by zero. With large ovals and low precision this
1873 // leads to blurring at the edge of the oval.
1874 const SkScalar kMaxOvalRadius = 16384;
1875 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1876 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1877 return nullptr;
1878 }
1879
Greg Daniel2655ede2019-04-10 00:49:28 +00001880 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001881 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001882 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001883
Brian Osmancf860852018-10-31 14:04:39 -04001884 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Greg Daniel2655ede2019-04-10 00:49:28 +00001885 const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001886 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001887 : INHERITED(ClassID())
1888 , fHelper(helperArgs, GrAAType::kCoverage)
1889 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001890 SkStrokeRec::Style style = stroke.getStyle();
1891 bool isStrokeOnly =
1892 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001893
Brian Salomon05441c42017-05-15 16:45:49 -04001894 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1895 params.fInnerXRadius, params.fInnerYRadius,
1896 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1897 params.fCenter.fY - params.fYRadius,
1898 params.fCenter.fX + params.fXRadius,
1899 params.fCenter.fY + params.fYRadius)});
1900
Greg Daniel5faf4742019-10-01 15:14:44 -04001901 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001902
bsalomon4b4a7cc2016-07-08 04:42:54 -07001903 // Outset bounds to include half-pixel width antialiasing.
Greg Daniel2655ede2019-04-10 00:49:28 +00001904 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001905
Brian Salomon05441c42017-05-15 16:45:49 -04001906 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1907 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001908 }
joshualitt76e7fb62015-02-11 08:52:27 -08001909
Brian Salomon289e3d82016-12-14 15:52:56 -05001910 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001911
Chris Dalton1706cbf2019-05-21 19:35:29 -06001912 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05001913 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06001914 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05001915 } else {
1916 fHelper.visitProxies(func);
1917 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001918 }
1919
Chris Dalton6ce447a2019-06-23 18:07:38 -06001920 GrProcessorSet::Analysis finalize(
1921 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1922 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001923 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1924 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001925 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001926 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001927 GrProcessorAnalysisCoverage::kSingleChannel, color,
1928 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001929 }
1930
1931 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1932
bsalomone46f9fe2015-08-18 06:05:14 -07001933private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001934 GrProgramInfo* programInfo() override { return fProgramInfo; }
1935
Robert Phillips4133dc42020-03-11 15:55:55 -04001936 void onCreateProgramInfo(const GrCaps* caps,
1937 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001938 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001939 GrAppliedClip&& appliedClip,
1940 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001941 SkMatrix localMatrix;
1942 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001943 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001944 }
1945
Robert Phillips4490d922020-03-03 14:50:59 -05001946 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1947 fUseScale, localMatrix);
1948
Brian Salomon8afde5f2020-04-01 16:22:00 -04001949 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001950 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001951 }
1952
Robert Phillips4490d922020-03-03 14:50:59 -05001953 void onPrepareDraws(Target* target) override {
1954 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001955 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001956 if (!fProgramInfo) {
1957 return;
1958 }
1959 }
1960
1961 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001962 GrVertexWriter verts{helper.vertices()};
1963 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001964 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001965 return;
1966 }
1967
Brian Salomon05441c42017-05-15 16:45:49 -04001968 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001969 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001970 SkScalar xRadius = ellipse.fXRadius;
1971 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001972
1973 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001974 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1975 SkScalarInvert(xRadius),
1976 SkScalarInvert(yRadius),
1977 SkScalarInvert(ellipse.fInnerXRadius),
1978 SkScalarInvert(ellipse.fInnerYRadius)
1979 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001980 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1981 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001982
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001983 if (!fStroked) {
1984 // For filled ellipses we map a unit circle in the vertex attributes rather than
1985 // computing an ellipse and modifying that distance, so we normalize to 1
1986 xMaxOffset /= xRadius;
1987 yMaxOffset /= yRadius;
1988 }
1989
joshualitt76e7fb62015-02-11 08:52:27 -08001990 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001991 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1992 color,
1993 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05001994 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05001995 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001996 }
Robert Phillips4490d922020-03-03 14:50:59 -05001997 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001998 }
1999
2000 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002001 if (!fProgramInfo || !fMesh) {
2002 return;
2003 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002004
Chris Dalton765ed362020-03-16 17:34:44 -06002005 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2006 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2007 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002008 }
2009
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002010 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2011 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002012 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002013
Brian Salomon05441c42017-05-15 16:45:49 -04002014 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002015 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002016 }
2017
bsalomoncdaa97b2016-03-08 08:30:14 -08002018 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002019 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002020 }
2021
Brian Salomon05441c42017-05-15 16:45:49 -04002022 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002023 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2024 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002025 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002026 }
2027
Brian Salomon05441c42017-05-15 16:45:49 -04002028 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002029 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002030 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002031 }
2032
John Stilesaf366522020-08-13 09:57:34 -04002033#if GR_TEST_UTILS
2034 SkString onDumpInfo() const override {
2035 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
2036 for (const auto& geo : fEllipses) {
2037 string.appendf(
2038 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2039 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2040 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
2041 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2042 geo.fInnerXRadius, geo.fInnerYRadius);
2043 }
2044 string += fHelper.dumpInfo();
2045 return string;
2046 }
2047#endif
2048
Brian Salomon05441c42017-05-15 16:45:49 -04002049 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04002050 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002051 SkScalar fXRadius;
2052 SkScalar fYRadius;
2053 SkScalar fInnerXRadius;
2054 SkScalar fInnerYRadius;
2055 SkRect fDevBounds;
2056 };
joshualitt76e7fb62015-02-11 08:52:27 -08002057
Brian Salomon289e3d82016-12-14 15:52:56 -05002058 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002059 Helper fHelper;
2060 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002061 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002062 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002063 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002064
Chris Daltoneb694b72020-03-16 09:25:50 -06002065 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002066 GrProgramInfo* fProgramInfo = nullptr;
2067
Brian Salomon05441c42017-05-15 16:45:49 -04002068 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002069};
2070
joshualitt76e7fb62015-02-11 08:52:27 -08002071/////////////////////////////////////////////////////////////////////////////////////////////////
2072
Brian Salomon05441c42017-05-15 16:45:49 -04002073class DIEllipseOp : public GrMeshDrawOp {
2074private:
2075 using Helper = GrSimpleMeshDrawOpHelper;
2076
2077 struct DeviceSpaceParams {
2078 SkPoint fCenter;
2079 SkScalar fXRadius;
2080 SkScalar fYRadius;
2081 SkScalar fInnerXRadius;
2082 SkScalar fInnerYRadius;
2083 DIEllipseStyle fStyle;
2084 };
2085
joshualitt76e7fb62015-02-11 08:52:27 -08002086public:
Brian Salomon25a88092016-12-01 09:36:50 -05002087 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002088
Robert Phillipsb97da532019-02-12 15:24:12 -05002089 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002090 GrPaint&& paint,
2091 const SkMatrix& viewMatrix,
2092 const SkRect& ellipse,
2093 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002094 DeviceSpaceParams params;
2095 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2096 params.fXRadius = SkScalarHalf(ellipse.width());
2097 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002098
bsalomon4b4a7cc2016-07-08 04:42:54 -07002099 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002100 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2101 ? DIEllipseStyle::kStroke
2102 : (SkStrokeRec::kHairline_Style == style)
2103 ? DIEllipseStyle::kHairline
2104 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002105
Brian Salomon05441c42017-05-15 16:45:49 -04002106 params.fInnerXRadius = 0;
2107 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002108 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2109 SkScalar strokeWidth = stroke.getWidth();
2110
2111 if (SkScalarNearlyZero(strokeWidth)) {
2112 strokeWidth = SK_ScalarHalf;
2113 } else {
2114 strokeWidth *= SK_ScalarHalf;
2115 }
2116
2117 // we only handle thick strokes for near-circular ellipses
2118 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002119 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2120 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002121 return nullptr;
2122 }
2123
2124 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002125 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2126 (strokeWidth * strokeWidth) * params.fXRadius) {
2127 return nullptr;
2128 }
2129 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2130 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002131 return nullptr;
2132 }
2133
2134 // set inner radius (if needed)
2135 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002136 params.fInnerXRadius = params.fXRadius - strokeWidth;
2137 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002138 }
2139
Brian Salomon05441c42017-05-15 16:45:49 -04002140 params.fXRadius += strokeWidth;
2141 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002142 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002143
2144 // For large ovals with low precision floats, we fall back to the path renderer.
2145 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2146 // minimum value to avoid divides by zero. With large ovals and low precision this
2147 // leads to blurring at the edge of the oval.
2148 const SkScalar kMaxOvalRadius = 16384;
2149 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2150 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2151 return nullptr;
2152 }
2153
Brian Salomon05441c42017-05-15 16:45:49 -04002154 if (DIEllipseStyle::kStroke == params.fStyle &&
2155 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2156 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002157 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002158 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002159 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002160
Greg Daniel2655ede2019-04-10 00:49:28 +00002161 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002162 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002163 : INHERITED(ClassID())
2164 , fHelper(helperArgs, GrAAType::kCoverage)
2165 , fUseScale(false) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002166 // This expands the outer rect so that after CTM we end up with a half-pixel border
2167 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2168 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2169 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2170 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2171 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2172 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002173
Brian Salomon05441c42017-05-15 16:45:49 -04002174 fEllipses.emplace_back(
2175 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2176 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2177 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2178 params.fCenter.fY - params.fYRadius - geoDy,
2179 params.fCenter.fX + params.fXRadius + geoDx,
2180 params.fCenter.fY + params.fYRadius + geoDy)});
Greg Daniel2655ede2019-04-10 00:49:28 +00002181 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
Greg Daniel5faf4742019-10-01 15:14:44 -04002182 IsHairline::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002183 }
2184
Brian Salomon289e3d82016-12-14 15:52:56 -05002185 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002186
Chris Dalton1706cbf2019-05-21 19:35:29 -06002187 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002188 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002189 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002190 } else {
2191 fHelper.visitProxies(func);
2192 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002193 }
2194
Chris Dalton6ce447a2019-06-23 18:07:38 -06002195 GrProcessorSet::Analysis finalize(
2196 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2197 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002198 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2199 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002200 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002201 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002202 GrProcessorAnalysisCoverage::kSingleChannel, color,
2203 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002204 }
2205
2206 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2207
bsalomone46f9fe2015-08-18 06:05:14 -07002208private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002209 GrProgramInfo* programInfo() override { return fProgramInfo; }
2210
Robert Phillips4133dc42020-03-11 15:55:55 -04002211 void onCreateProgramInfo(const GrCaps* caps,
2212 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002213 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002214 GrAppliedClip&& appliedClip,
2215 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002216 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2217 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002218 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002219
Brian Salomon8afde5f2020-04-01 16:22:00 -04002220 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002221 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002222 }
2223
Robert Phillips4490d922020-03-03 14:50:59 -05002224 void onPrepareDraws(Target* target) override {
2225 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002226 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002227 }
2228
2229 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002230 GrVertexWriter verts{helper.vertices()};
2231 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002232 return;
2233 }
2234
Brian Salomon05441c42017-05-15 16:45:49 -04002235 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002236 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002237 SkScalar xRadius = ellipse.fXRadius;
2238 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002239
joshualitt76e7fb62015-02-11 08:52:27 -08002240 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002241 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2242 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002243
Brian Osman9d958b52018-11-13 12:46:56 -05002244 // By default, constructed so that inner offset is (0, 0) for all points
2245 SkScalar innerRatioX = -offsetDx;
2246 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002247
Brian Osman9d958b52018-11-13 12:46:56 -05002248 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002249 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002250 innerRatioX = xRadius / ellipse.fInnerXRadius;
2251 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002252 }
joshualitt76e7fb62015-02-11 08:52:27 -08002253
Brian Osman2b6e3902018-11-21 15:29:43 -05002254 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2255 color,
2256 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002257 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002258 origin_centered_tri_strip(innerRatioX + offsetDx,
2259 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002260 }
Robert Phillips4490d922020-03-03 14:50:59 -05002261 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002262 }
2263
2264 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002265 if (!fProgramInfo || !fMesh) {
2266 return;
2267 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002268
Chris Dalton765ed362020-03-16 17:34:44 -06002269 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2270 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2271 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002272 }
halcanary9d524f22016-03-29 09:03:52 -07002273
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002274 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2275 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002276 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002277 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002278 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002279 }
2280
bsalomoncdaa97b2016-03-08 08:30:14 -08002281 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002282 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002283 }
2284
joshualittd96a67b2015-05-05 14:09:05 -07002285 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002286 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002287 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002288 }
2289
Brian Salomon05441c42017-05-15 16:45:49 -04002290 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002291 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002292 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002293 }
2294
John Stilesaf366522020-08-13 09:57:34 -04002295#if GR_TEST_UTILS
2296 SkString onDumpInfo() const override {
2297 SkString string;
2298 for (const auto& geo : fEllipses) {
2299 string.appendf(
2300 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2301 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2302 "GeoDY: %.2f\n",
2303 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
2304 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2305 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
2306 }
2307 string += fHelper.dumpInfo();
2308 return string;
2309 }
2310#endif
2311
Brian Salomon05441c42017-05-15 16:45:49 -04002312 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2313 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002314
Brian Salomon05441c42017-05-15 16:45:49 -04002315 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002316 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002317 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002318 SkScalar fXRadius;
2319 SkScalar fYRadius;
2320 SkScalar fInnerXRadius;
2321 SkScalar fInnerYRadius;
2322 SkScalar fGeoDx;
2323 SkScalar fGeoDy;
2324 DIEllipseStyle fStyle;
2325 SkRect fBounds;
2326 };
2327
Brian Salomon05441c42017-05-15 16:45:49 -04002328 Helper fHelper;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002329 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002330 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002331 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002332
Chris Daltoneb694b72020-03-16 09:25:50 -06002333 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002334 GrProgramInfo* fProgramInfo = nullptr;
2335
Brian Salomon05441c42017-05-15 16:45:49 -04002336 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002337};
2338
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002339///////////////////////////////////////////////////////////////////////////////
2340
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002341// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002342//
2343// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2344// ____________
2345// |_|________|_|
2346// | | | |
2347// | | | |
2348// | | | |
2349// |_|________|_|
2350// |_|________|_|
2351//
2352// For strokes, we don't draw the center quad.
2353//
2354// For circular roundrects, in the case where the stroke width is greater than twice
2355// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002356// in the center. The shared vertices are duplicated so we can set a different outer radius
2357// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002358// ____________
2359// |_|________|_|
2360// | |\ ____ /| |
2361// | | | | | |
2362// | | |____| | |
2363// |_|/______\|_|
2364// |_|________|_|
2365//
2366// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002367//
2368// For filled rrects that need to provide a distance vector we resuse the overstroke
2369// geometry but make the inner rect degenerate (either a point or a horizontal or
2370// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002371
jvanverth84839f62016-08-29 10:16:40 -07002372static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002373 // clang-format off
2374 // overstroke quads
2375 // we place this at the beginning so that we can skip these indices when rendering normally
2376 16, 17, 19, 16, 19, 18,
2377 19, 17, 23, 19, 23, 21,
2378 21, 23, 22, 21, 22, 20,
2379 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002380
Brian Salomon289e3d82016-12-14 15:52:56 -05002381 // corners
2382 0, 1, 5, 0, 5, 4,
2383 2, 3, 7, 2, 7, 6,
2384 8, 9, 13, 8, 13, 12,
2385 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002386
Brian Salomon289e3d82016-12-14 15:52:56 -05002387 // edges
2388 1, 2, 6, 1, 6, 5,
2389 4, 5, 9, 4, 9, 8,
2390 6, 7, 11, 6, 11, 10,
2391 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002392
Brian Salomon289e3d82016-12-14 15:52:56 -05002393 // center
2394 // we place this at the end so that we can ignore these indices when not rendering as filled
2395 5, 6, 10, 5, 10, 9,
2396 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002397};
Brian Salomon289e3d82016-12-14 15:52:56 -05002398
jvanverth84839f62016-08-29 10:16:40 -07002399// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002400static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002401
jvanverth84839f62016-08-29 10:16:40 -07002402// overstroke count is arraysize minus the center indices
2403static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2404// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002405static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002406// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002407static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2408static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002409static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002410
jvanverthc3d0e422016-08-25 08:12:35 -07002411enum RRectType {
2412 kFill_RRectType,
2413 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002414 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002415};
2416
jvanverth84839f62016-08-29 10:16:40 -07002417static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002418 switch (type) {
2419 case kFill_RRectType:
2420 case kStroke_RRectType:
2421 return kVertsPerStandardRRect;
2422 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002423 return kVertsPerOverstrokeRRect;
2424 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002425 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002426}
2427
2428static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002429 switch (type) {
2430 case kFill_RRectType:
2431 return kIndicesPerFillRRect;
2432 case kStroke_RRectType:
2433 return kIndicesPerStrokeRRect;
2434 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002435 return kIndicesPerOverstrokeRRect;
2436 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002437 SK_ABORT("Invalid type");
jvanverth84839f62016-08-29 10:16:40 -07002438}
2439
2440static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002441 switch (type) {
2442 case kFill_RRectType:
2443 case kStroke_RRectType:
2444 return gStandardRRectIndices;
2445 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002446 return gOverstrokeRRectIndices;
2447 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002448 SK_ABORT("Invalid type");
bsalomoned0bcad2015-05-04 10:36:42 -07002449}
2450
joshualitt76e7fb62015-02-11 08:52:27 -08002451///////////////////////////////////////////////////////////////////////////////////////////////////
2452
Robert Phillips79839d42016-10-06 15:03:34 -04002453// For distance computations in the interior of filled rrects we:
2454//
2455// add a interior degenerate (point or line) rect
2456// each vertex of that rect gets -outerRad as its radius
2457// this makes the computation of the distance to the outer edge be negative
2458// negative values are caught and then handled differently in the GP's onEmitCode
2459// each vertex is also given the normalized x & y distance from the interior rect's edge
2460// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2461
Brian Salomon05441c42017-05-15 16:45:49 -04002462class CircularRRectOp : public GrMeshDrawOp {
2463private:
2464 using Helper = GrSimpleMeshDrawOpHelper;
2465
joshualitt76e7fb62015-02-11 08:52:27 -08002466public:
Brian Salomon25a88092016-12-01 09:36:50 -05002467 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002468
bsalomon4b4a7cc2016-07-08 04:42:54 -07002469 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2470 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002471 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002472 GrPaint&& paint,
2473 const SkMatrix& viewMatrix,
2474 const SkRect& devRect,
2475 float devRadius,
2476 float devStrokeWidth,
2477 bool strokeOnly) {
Greg Daniel2655ede2019-04-10 00:49:28 +00002478 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002479 devRect, devRadius,
2480 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002481 }
Greg Daniel2655ede2019-04-10 00:49:28 +00002482 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002483 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2484 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002485 : INHERITED(ClassID())
2486 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Greg Daniel2655ede2019-04-10 00:49:28 +00002487 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002488 SkRect bounds = devRect;
2489 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2490 SkScalar innerRadius = 0.0f;
2491 SkScalar outerRadius = devRadius;
2492 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002493 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002494 if (devStrokeWidth > 0) {
2495 if (SkScalarNearlyZero(devStrokeWidth)) {
2496 halfWidth = SK_ScalarHalf;
2497 } else {
2498 halfWidth = SkScalarHalf(devStrokeWidth);
2499 }
joshualitt76e7fb62015-02-11 08:52:27 -08002500
bsalomon4b4a7cc2016-07-08 04:42:54 -07002501 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002502 // Outset stroke by 1/4 pixel
2503 devStrokeWidth += 0.25f;
2504 // If stroke is greater than width or height, this is still a fill
2505 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002506 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002507 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002508 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002509 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002510 }
2511 outerRadius += halfWidth;
2512 bounds.outset(halfWidth, halfWidth);
2513 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002514
Greg Daniel2655ede2019-04-10 00:49:28 +00002515 // The radii are outset for two reasons. First, it allows the shader to simply perform
2516 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2517 // Second, the outer radius is used to compute the verts of the bounding box that is
2518 // rendered and the outset ensures the box will cover all partially covered by the rrect
2519 // corners.
2520 outerRadius += SK_ScalarHalf;
2521 innerRadius -= SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002522
Greg Daniel5faf4742019-10-01 15:14:44 -04002523 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002524
Greg Daniel2655ede2019-04-10 00:49:28 +00002525 // Expand the rect for aa to generate correct vertices.
2526 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002527
Brian Salomon05441c42017-05-15 16:45:49 -04002528 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002529 fVertCount = rrect_type_to_vert_count(type);
2530 fIndexCount = rrect_type_to_index_count(type);
2531 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002532 }
2533
Brian Salomon289e3d82016-12-14 15:52:56 -05002534 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002535
Chris Dalton1706cbf2019-05-21 19:35:29 -06002536 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002537 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002538 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002539 } else {
2540 fHelper.visitProxies(func);
2541 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002542 }
2543
Chris Dalton6ce447a2019-06-23 18:07:38 -06002544 GrProcessorSet::Analysis finalize(
2545 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2546 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002547 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002548 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002549 GrProcessorAnalysisCoverage::kSingleChannel, color,
2550 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002551 }
2552
2553 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2554
Brian Salomon92aee3d2016-12-21 09:20:25 -05002555private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002556 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002557 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002558 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002559 SkASSERT(smInset < bigInset);
2560
2561 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002562 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2563 color,
2564 xOffset, 0.0f,
2565 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002566
2567 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002568 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2569 color,
2570 xOffset, 0.0f,
2571 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002572
Brian Osmana1d4eb92018-12-06 16:33:10 -05002573 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2574 color,
2575 0.0f, 0.0f,
2576 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002577
Brian Osmana1d4eb92018-12-06 16:33:10 -05002578 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2579 color,
2580 0.0f, 0.0f,
2581 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002582
Brian Osmana1d4eb92018-12-06 16:33:10 -05002583 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2584 color,
2585 0.0f, 0.0f,
2586 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002587
Brian Osmana1d4eb92018-12-06 16:33:10 -05002588 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2589 color,
2590 0.0f, 0.0f,
2591 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002592
2593 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002594 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2595 color,
2596 xOffset, 0.0f,
2597 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002598
2599 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002600 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2601 color,
2602 xOffset, 0.0f,
2603 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002604 }
2605
Robert Phillips2669a7b2020-03-12 12:07:19 -04002606 GrProgramInfo* programInfo() override { return fProgramInfo; }
2607
Robert Phillips4133dc42020-03-11 15:55:55 -04002608 void onCreateProgramInfo(const GrCaps* caps,
2609 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002610 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002611 GrAppliedClip&& appliedClip,
2612 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002613 // Invert the view matrix as a local matrix (if any other processors require coords).
2614 SkMatrix localMatrix;
2615 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002616 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002617 }
2618
Robert Phillips4490d922020-03-03 14:50:59 -05002619 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002620 false, false, false, false,
2621 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002622
Brian Salomon8afde5f2020-04-01 16:22:00 -04002623 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002624 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002625 }
2626
Robert Phillips4490d922020-03-03 14:50:59 -05002627 void onPrepareDraws(Target* target) override {
2628 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002629 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002630 if (!fProgramInfo) {
2631 return;
2632 }
2633 }
2634
Brian Salomon12d22642019-01-29 14:38:50 -05002635 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002636 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002637
Robert Phillips4490d922020-03-03 14:50:59 -05002638 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2639 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002640 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002641 SkDebugf("Could not allocate vertices\n");
2642 return;
2643 }
2644
Brian Salomon12d22642019-01-29 14:38:50 -05002645 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002646 int firstIndex = 0;
2647 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2648 if (!indices) {
2649 SkDebugf("Could not allocate indices\n");
2650 return;
2651 }
2652
2653 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002654 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002655 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002656 SkScalar outerRadius = rrect.fOuterRadius;
2657 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002658
Brian Salomon289e3d82016-12-14 15:52:56 -05002659 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2660 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002661
Brian Salomon289e3d82016-12-14 15:52:56 -05002662 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002663 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002664 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002665 SkScalar innerRadius = rrect.fType != kFill_RRectType
2666 ? rrect.fInnerRadius / rrect.fOuterRadius
2667 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002668 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002669 verts.write(bounds.fLeft, yCoords[i],
2670 color,
2671 -1.0f, yOuterRadii[i],
2672 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002673
Brian Osmana1d4eb92018-12-06 16:33:10 -05002674 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2675 color,
2676 0.0f, yOuterRadii[i],
2677 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002678
Brian Osmana1d4eb92018-12-06 16:33:10 -05002679 verts.write(bounds.fRight - outerRadius, yCoords[i],
2680 color,
2681 0.0f, yOuterRadii[i],
2682 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002683
Brian Osmana1d4eb92018-12-06 16:33:10 -05002684 verts.write(bounds.fRight, yCoords[i],
2685 color,
2686 1.0f, yOuterRadii[i],
2687 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002688 }
jvanverthc3d0e422016-08-25 08:12:35 -07002689 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002690 // Effectively this is an additional stroked rrect, with its
2691 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2692 // This will give us correct AA in the center and the correct
2693 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002694 //
jvanvertha4f1af82016-08-29 07:17:47 -07002695 // Also, the outer offset is a constant vector pointing to the right, which
2696 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002697 if (kOverstroke_RRectType == rrect.fType) {
2698 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002699
Brian Salomon05441c42017-05-15 16:45:49 -04002700 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002701 // this is the normalized distance from the outer rectangle of this
2702 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002703 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002704
Brian Osmana1d4eb92018-12-06 16:33:10 -05002705 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002706 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002707 }
jvanverth6a397612016-08-26 08:15:33 -07002708
Brian Salomon05441c42017-05-15 16:45:49 -04002709 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2710 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002711 for (int i = 0; i < primIndexCount; ++i) {
2712 *indices++ = primIndices[i] + currStartVertex;
2713 }
2714
Brian Salomon05441c42017-05-15 16:45:49 -04002715 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002716 }
2717
Robert Phillips4490d922020-03-03 14:50:59 -05002718 fMesh = target->allocMesh();
2719 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002720 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002721 }
2722
2723 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002724 if (!fProgramInfo || !fMesh) {
2725 return;
2726 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002727
Chris Dalton765ed362020-03-16 17:34:44 -06002728 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2729 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2730 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002731 }
2732
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002733 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2734 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002735 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002736
2737 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002738 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002739 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002740 }
2741
Brian Salomon05441c42017-05-15 16:45:49 -04002742 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002743 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002744 }
2745
Brian Salomon05441c42017-05-15 16:45:49 -04002746 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002747 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2748 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002749 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002750 }
2751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002753 fVertCount += that->fVertCount;
2754 fIndexCount += that->fIndexCount;
2755 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002756 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002757 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002758 }
2759
John Stilesaf366522020-08-13 09:57:34 -04002760#if GR_TEST_UTILS
2761 SkString onDumpInfo() const override {
2762 SkString string;
2763 for (int i = 0; i < fRRects.count(); ++i) {
2764 string.appendf(
2765 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2766 "InnerRad: %.2f, OuterRad: %.2f\n",
2767 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
2768 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2769 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2770 fRRects[i].fOuterRadius);
2771 }
2772 string += fHelper.dumpInfo();
2773 return string;
2774 }
2775#endif
2776
Brian Salomon05441c42017-05-15 16:45:49 -04002777 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002778 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002779 SkScalar fInnerRadius;
2780 SkScalar fOuterRadius;
2781 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002782 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002783 };
2784
Brian Salomon289e3d82016-12-14 15:52:56 -05002785 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002786 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002787 int fVertCount;
2788 int fIndexCount;
2789 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002790 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002791 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002792
Chris Daltoneb694b72020-03-16 09:25:50 -06002793 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05002794 GrProgramInfo* fProgramInfo = nullptr;
2795
Brian Salomon05441c42017-05-15 16:45:49 -04002796 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002797};
2798
jvanverth84839f62016-08-29 10:16:40 -07002799static const int kNumRRectsInIndexBuffer = 256;
2800
2801GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2802GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002803static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2804 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002805 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2806 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2807 switch (type) {
2808 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002809 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002810 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2811 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002812 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002813 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002814 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2815 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002816 default:
2817 SkASSERT(false);
2818 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002819 }
jvanverth84839f62016-08-29 10:16:40 -07002820}
2821
Brian Salomon05441c42017-05-15 16:45:49 -04002822class EllipticalRRectOp : public GrMeshDrawOp {
2823private:
2824 using Helper = GrSimpleMeshDrawOpHelper;
2825
joshualitt76e7fb62015-02-11 08:52:27 -08002826public:
Brian Salomon25a88092016-12-01 09:36:50 -05002827 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002828
bsalomon4b4a7cc2016-07-08 04:42:54 -07002829 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2830 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002831 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002832 GrPaint&& paint,
2833 const SkMatrix& viewMatrix,
2834 const SkRect& devRect,
2835 float devXRadius,
2836 float devYRadius,
2837 SkVector devStrokeWidths,
2838 bool strokeOnly) {
Brian Salomon182ce662020-08-13 11:29:42 -04002839 SkASSERT(devXRadius >= 0.5 || strokeOnly);
2840 SkASSERT(devYRadius >= 0.5 || strokeOnly);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002841 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2842 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002843 if (devStrokeWidths.fX > 0) {
2844 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2845 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2846 } else {
2847 devStrokeWidths.scale(SK_ScalarHalf);
2848 }
joshualitt76e7fb62015-02-11 08:52:27 -08002849
bsalomon4b4a7cc2016-07-08 04:42:54 -07002850 // we only handle thick strokes for near-circular ellipses
2851 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002852 (SK_ScalarHalf * devXRadius > devYRadius ||
2853 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002854 return nullptr;
2855 }
2856
2857 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002858 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2859 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002860 return nullptr;
2861 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002862 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2863 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002864 return nullptr;
2865 }
Brian Salomon05441c42017-05-15 16:45:49 -04002866 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002867 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Greg Daniel2655ede2019-04-10 00:49:28 +00002868 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002869 devXRadius, devYRadius, devStrokeWidths,
2870 strokeOnly);
2871 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002872
Greg Daniel2655ede2019-04-10 00:49:28 +00002873 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
Brian Osman936fe7d2018-10-30 15:30:35 -04002874 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2875 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002876 : INHERITED(ClassID())
2877 , fHelper(helperArgs, GrAAType::kCoverage)
2878 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002879 SkScalar innerXRadius = 0.0f;
2880 SkScalar innerYRadius = 0.0f;
2881 SkRect bounds = devRect;
2882 bool stroked = false;
2883 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002884 // this is legit only if scale & translation (which should be the case at the moment)
2885 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002886 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2887 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002888 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2889 }
2890
Brian Salomon05441c42017-05-15 16:45:49 -04002891 devXRadius += devStrokeHalfWidths.fX;
2892 devYRadius += devStrokeHalfWidths.fY;
2893 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002894 }
2895
Brian Salomon05441c42017-05-15 16:45:49 -04002896 fStroked = stroked;
2897 fViewMatrixIfUsingLocalCoords = viewMatrix;
Greg Daniel5faf4742019-10-01 15:14:44 -04002898 this->setBounds(bounds, HasAABloat::kYes, IsHairline::kNo);
Greg Daniel2655ede2019-04-10 00:49:28 +00002899 // Expand the rect for aa in order to generate the correct vertices.
2900 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002901 fRRects.emplace_back(
2902 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002903 }
2904
Brian Salomon289e3d82016-12-14 15:52:56 -05002905 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002906
Chris Dalton1706cbf2019-05-21 19:35:29 -06002907 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillips4490d922020-03-03 14:50:59 -05002908 if (fProgramInfo) {
Chris Daltonbe457422020-03-16 18:05:03 -06002909 fProgramInfo->visitFPProxies(func);
Robert Phillips4490d922020-03-03 14:50:59 -05002910 } else {
2911 fHelper.visitProxies(func);
2912 }
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002913 }
2914
Chris Dalton6ce447a2019-06-23 18:07:38 -06002915 GrProcessorSet::Analysis finalize(
2916 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2917 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002918 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002919 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002920 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002921 GrProcessorAnalysisCoverage::kSingleChannel, color,
2922 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002923 }
2924
2925 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2926
bsalomone46f9fe2015-08-18 06:05:14 -07002927private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002928 GrProgramInfo* programInfo() override { return fProgramInfo; }
2929
Robert Phillips4133dc42020-03-11 15:55:55 -04002930 void onCreateProgramInfo(const GrCaps* caps,
2931 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002932 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002933 GrAppliedClip&& appliedClip,
2934 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002935 SkMatrix localMatrix;
2936 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002937 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002938 }
2939
Robert Phillips4490d922020-03-03 14:50:59 -05002940 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2941 fUseScale, localMatrix);
2942
Brian Salomon8afde5f2020-04-01 16:22:00 -04002943 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002944 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002945 }
2946
Robert Phillips4490d922020-03-03 14:50:59 -05002947 void onPrepareDraws(Target* target) override {
2948 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002949 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002950 if (!fProgramInfo) {
2951 return;
2952 }
2953 }
joshualitt76e7fb62015-02-11 08:52:27 -08002954
bsalomonb5238a72015-05-05 07:49:49 -07002955 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002956 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002957 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2958 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002959
Brian Salomon12d22642019-01-29 14:38:50 -05002960 if (!indexBuffer) {
2961 SkDebugf("Could not allocate indices\n");
2962 return;
2963 }
Robert Phillips4490d922020-03-03 14:50:59 -05002964 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2965 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002966 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002967 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002968 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002969 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002970 SkDebugf("Could not allocate vertices\n");
2971 return;
2972 }
2973
Brian Salomon05441c42017-05-15 16:45:49 -04002974 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002975 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002976 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002977 float reciprocalRadii[4] = {
2978 SkScalarInvert(rrect.fXRadius),
2979 SkScalarInvert(rrect.fYRadius),
2980 SkScalarInvert(rrect.fInnerXRadius),
2981 SkScalarInvert(rrect.fInnerYRadius)
2982 };
joshualitt76e7fb62015-02-11 08:52:27 -08002983
2984 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00002985 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2986 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002987
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002988 SkScalar xMaxOffset = xOuterRadius;
2989 SkScalar yMaxOffset = yOuterRadius;
2990 if (!fStroked) {
2991 // For filled rrects we map a unit circle in the vertex attributes rather than
2992 // computing an ellipse and modifying that distance, so we normalize to 1.
2993 xMaxOffset /= rrect.fXRadius;
2994 yMaxOffset /= rrect.fYRadius;
2995 }
2996
Brian Salomon05441c42017-05-15 16:45:49 -04002997 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002998
Brian Salomon289e3d82016-12-14 15:52:56 -05002999 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3000 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003001 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003002 SK_ScalarNearlyZero, // we're using inversesqrt() in
3003 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003004 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003005
Brian Osman788b9162020-02-07 10:36:46 -05003006 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003007 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003008 verts.write(bounds.fLeft, yCoords[i],
3009 color,
3010 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003011 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003012 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003013
Brian Osmana1d4eb92018-12-06 16:33:10 -05003014 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3015 color,
3016 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003017 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003018 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003019
Brian Osmana1d4eb92018-12-06 16:33:10 -05003020 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3021 color,
3022 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003023 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003024 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003025
Brian Osmana1d4eb92018-12-06 16:33:10 -05003026 verts.write(bounds.fRight, yCoords[i],
3027 color,
3028 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003029 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003030 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003031 }
3032 }
Robert Phillips4490d922020-03-03 14:50:59 -05003033 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003034 }
3035
3036 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003037 if (!fProgramInfo || !fMesh) {
3038 return;
3039 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003040
Chris Dalton765ed362020-03-16 17:34:44 -06003041 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3042 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3043 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003044 }
3045
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003046 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3047 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003048 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003049
Brian Salomon05441c42017-05-15 16:45:49 -04003050 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003051 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003052 }
3053
bsalomoncdaa97b2016-03-08 08:30:14 -08003054 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003055 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003056 }
3057
Brian Salomon05441c42017-05-15 16:45:49 -04003058 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003059 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3060 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003061 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003062 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003063
Brian Salomon05441c42017-05-15 16:45:49 -04003064 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003065 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003066 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003067 }
3068
John Stilesaf366522020-08-13 09:57:34 -04003069#if GR_TEST_UTILS
3070 SkString onDumpInfo() const override {
3071 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
3072 for (const auto& geo : fRRects) {
3073 string.appendf(
3074 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
3075 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
3076 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
3077 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
3078 geo.fInnerXRadius, geo.fInnerYRadius);
3079 }
3080 string += fHelper.dumpInfo();
3081 return string;
3082 }
3083#endif
3084
Brian Salomon05441c42017-05-15 16:45:49 -04003085 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04003086 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003087 SkScalar fXRadius;
3088 SkScalar fYRadius;
3089 SkScalar fInnerXRadius;
3090 SkScalar fInnerYRadius;
3091 SkRect fDevBounds;
3092 };
3093
Brian Salomon289e3d82016-12-14 15:52:56 -05003094 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003095 Helper fHelper;
3096 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05003097 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003098 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04003099 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003100
Chris Daltoneb694b72020-03-16 09:25:50 -06003101 GrSimpleMesh* fMesh = nullptr;
Robert Phillips4490d922020-03-03 14:50:59 -05003102 GrProgramInfo* fProgramInfo = nullptr;
3103
Brian Salomon05441c42017-05-15 16:45:49 -04003104 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003105};
3106
Jim Van Verth64b85892019-06-17 12:01:46 -04003107std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
3108 GrPaint&& paint,
3109 const SkMatrix& viewMatrix,
3110 const SkRRect& rrect,
3111 const SkStrokeRec& stroke,
3112 const GrShaderCaps* shaderCaps) {
3113 SkASSERT(viewMatrix.rectStaysRect());
3114 SkASSERT(viewMatrix.isSimilarity());
3115 SkASSERT(rrect.isSimple());
3116 SkASSERT(!rrect.isOval());
3117 SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
3118
3119 // RRect ops only handle simple, but not too simple, rrects.
3120 // Do any matrix crunching before we reset the draw state for device coords.
3121 const SkRect& rrectBounds = rrect.getBounds();
3122 SkRect bounds;
3123 viewMatrix.mapRect(&bounds, rrectBounds);
3124
3125 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
3126 SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
3127 viewMatrix[SkMatrix::kMSkewY]));
3128
3129 // Do mapping of stroke. Use -1 to indicate fill-only draws.
3130 SkScalar scaledStroke = -1;
3131 SkScalar strokeWidth = stroke.getWidth();
3132 SkStrokeRec::Style style = stroke.getStyle();
3133
3134 bool isStrokeOnly =
3135 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
3136 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3137
3138 if (hasStroke) {
3139 if (SkStrokeRec::kHairline_Style == style) {
3140 scaledStroke = SK_Scalar1;
3141 } else {
Robert Phillips4490d922020-03-03 14:50:59 -05003142 scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] +
3143 viewMatrix[SkMatrix::kMSkewY]));
Jim Van Verth64b85892019-06-17 12:01:46 -04003144 }
3145 }
3146
3147 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3148 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3149 // patch will have fractional coverage. This only matters when the interior is actually filled.
3150 // We could consider falling back to rect rendering here, since a tiny radius is
3151 // indistinguishable from a square corner.
3152 if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
3153 return nullptr;
3154 }
3155
3156 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
3157 scaledStroke, isStrokeOnly);
3158}
3159
Robert Phillipsb97da532019-02-12 15:24:12 -05003160static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003161 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003162 const SkMatrix& viewMatrix,
3163 const SkRRect& rrect,
3164 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003165 SkASSERT(viewMatrix.rectStaysRect());
3166 SkASSERT(rrect.isSimple());
3167 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003168
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003169 // RRect ops only handle simple, but not too simple, rrects.
3170 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003171 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003172 SkRect bounds;
3173 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003174
Mike Reed242135a2018-02-22 13:41:39 -05003175 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003176 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3177 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3178 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3179 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003180
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003181 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003182
bsalomon4b4a7cc2016-07-08 04:42:54 -07003183 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3184 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003185 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003186
Brian Salomon289e3d82016-12-14 15:52:56 -05003187 bool isStrokeOnly =
3188 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003189 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3190
3191 if (hasStroke) {
3192 if (SkStrokeRec::kHairline_Style == style) {
3193 scaledStroke.set(1, 1);
3194 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003195 scaledStroke.fX = SkScalarAbs(
3196 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3197 scaledStroke.fY = SkScalarAbs(
3198 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003199 }
3200
Jim Van Verth64b85892019-06-17 12:01:46 -04003201 // if half of strokewidth is greater than radius, we don't handle that right now
3202 if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
3203 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003204 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003205 }
3206 }
3207
Brian Salomon8a97f562019-04-18 14:07:27 -04003208 // The matrix may have a rotation by an odd multiple of 90 degrees.
Jim Van Verth64b85892019-06-17 12:01:46 -04003209 if (viewMatrix.getScaleX() == 0) {
Brian Salomon8a97f562019-04-18 14:07:27 -04003210 std::swap(xRadius, yRadius);
3211 std::swap(scaledStroke.fX, scaledStroke.fY);
3212 }
3213
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003214 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3215 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3216 // patch will have fractional coverage. This only matters when the interior is actually filled.
3217 // We could consider falling back to rect rendering here, since a tiny radius is
3218 // indistinguishable from a square corner.
3219 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003220 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003221 }
3222
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003223 // if the corners are circles, use the circle renderer
Jim Van Verth64b85892019-06-17 12:01:46 -04003224 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3225 xRadius, yRadius, scaledStroke, isStrokeOnly);
joshualitt3e708c52015-04-30 13:49:27 -07003226}
3227
Robert Phillipsb97da532019-02-12 15:24:12 -05003228std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003229 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003230 const SkMatrix& viewMatrix,
3231 const SkRRect& rrect,
3232 const SkStrokeRec& stroke,
3233 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003234 if (rrect.isOval()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003235 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003236 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003237 }
3238
3239 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003240 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003241 }
3242
Greg Daniel2655ede2019-04-10 00:49:28 +00003243 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003244}
joshualitt3e708c52015-04-30 13:49:27 -07003245
bsalomon4b4a7cc2016-07-08 04:42:54 -07003246///////////////////////////////////////////////////////////////////////////////
3247
Jim Van Verth64b85892019-06-17 12:01:46 -04003248std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
3249 GrPaint&& paint,
3250 const SkMatrix& viewMatrix,
3251 const SkRect& oval,
3252 const GrStyle& style,
3253 const GrShaderCaps* shaderCaps) {
3254 SkScalar width = oval.width();
3255 SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3256 circle_stays_circle(viewMatrix));
3257
3258 auto r = width / 2.f;
3259 SkPoint center = { oval.centerX(), oval.centerY() };
3260 if (style.hasNonDashPathEffect()) {
3261 return nullptr;
3262 } else if (style.isDashed()) {
3263 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3264 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3265 return nullptr;
3266 }
3267 auto onInterval = style.dashIntervals()[0];
3268 auto offInterval = style.dashIntervals()[1];
3269 if (offInterval == 0) {
3270 GrStyle strokeStyle(style.strokeRec(), nullptr);
3271 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3272 strokeStyle, shaderCaps);
3273 } else if (onInterval == 0) {
3274 // There is nothing to draw but we have no way to indicate that here.
3275 return nullptr;
3276 }
3277 auto angularOnInterval = onInterval / r;
3278 auto angularOffInterval = offInterval / r;
3279 auto phaseAngle = style.dashPhase() / r;
3280 // Currently this function doesn't accept ovals with different start angles, though
3281 // it could.
3282 static const SkScalar kStartAngle = 0.f;
3283 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
3284 style.strokeRec().getWidth(), kStartAngle,
3285 angularOnInterval, angularOffInterval, phaseAngle);
3286 }
3287 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
3288}
3289
Robert Phillipsb97da532019-02-12 15:24:12 -05003290std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003291 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003292 const SkMatrix& viewMatrix,
3293 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003294 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003295 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003296 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003297 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003298 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3299 circle_stays_circle(viewMatrix)) {
Jim Van Verth64b85892019-06-17 12:01:46 -04003300 return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003301 }
3302
3303 if (style.pathEffect()) {
3304 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003305 }
3306
Stan Ilieveb868aa2017-02-21 11:06:16 -05003307 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003308 if (viewMatrix.rectStaysRect()) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003309 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003310 }
3311
Stan Ilieveb868aa2017-02-21 11:06:16 -05003312 // Otherwise, if we have shader derivative support, render as device-independent
3313 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003314 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3315 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3316 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3317 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3318 // Check for near-degenerate matrix
3319 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Greg Daniel2655ede2019-04-10 00:49:28 +00003320 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003321 style.strokeRec());
3322 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003323 }
3324
bsalomon4b4a7cc2016-07-08 04:42:54 -07003325 return nullptr;
3326}
3327
3328///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003329
Robert Phillipsb97da532019-02-12 15:24:12 -05003330std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003331 GrPaint&& paint,
3332 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003333 const SkRect& oval, SkScalar startAngle,
3334 SkScalar sweepAngle, bool useCenter,
3335 const GrStyle& style,
3336 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003337 SkASSERT(!oval.isEmpty());
3338 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003339 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003340 if (SkScalarAbs(sweepAngle) >= 360.f) {
3341 return nullptr;
3342 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003343 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3344 return nullptr;
3345 }
3346 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003347 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3348 useCenter};
Greg Daniel2655ede2019-04-10 00:49:28 +00003349 return CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003350 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003351}
3352
3353///////////////////////////////////////////////////////////////////////////////
3354
Hal Canary6f6961e2017-01-31 13:50:44 -05003355#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003356
Brian Salomon05441c42017-05-15 16:45:49 -04003357GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003358 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003359 SkScalar rotate = random->nextSScalar1() * 360.f;
3360 SkScalar translateX = random->nextSScalar1() * 1000.f;
3361 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003362 SkScalar scale;
3363 do {
3364 scale = random->nextSScalar1() * 100.f;
3365 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003366 SkMatrix viewMatrix;
3367 viewMatrix.setRotate(rotate);
3368 viewMatrix.postTranslate(translateX, translateY);
3369 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003370 SkRect circle = GrTest::TestSquare(random);
3371 SkPoint center = {circle.centerX(), circle.centerY()};
3372 SkScalar radius = circle.width() / 2.f;
3373 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003374 CircleOp::ArcParams arcParamsTmp;
3375 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003376 if (random->nextBool()) {
3377 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003378 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3379 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003380 arcParams = &arcParamsTmp;
3381 }
Greg Daniel2655ede2019-04-10 00:49:28 +00003382 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003383 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003384 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003385 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003386 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003387 }
Mike Klein16885072018-12-11 09:54:31 -05003388 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003389 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003390}
3391
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003392GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3393 SkScalar rotate = random->nextSScalar1() * 360.f;
3394 SkScalar translateX = random->nextSScalar1() * 1000.f;
3395 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003396 SkScalar scale;
3397 do {
3398 scale = random->nextSScalar1() * 100.f;
3399 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003400 SkMatrix viewMatrix;
3401 viewMatrix.setRotate(rotate);
3402 viewMatrix.postTranslate(translateX, translateY);
3403 viewMatrix.postScale(scale, scale);
3404 SkRect circle = GrTest::TestSquare(random);
3405 SkPoint center = {circle.centerX(), circle.centerY()};
3406 SkScalar radius = circle.width() / 2.f;
3407 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3408 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3409 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3410 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3411 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003412 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3413 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003414 startAngle, onAngle, offAngle, phase);
3415}
3416
Brian Salomon05441c42017-05-15 16:45:49 -04003417GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003418 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003419 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003420 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003421 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003422}
3423
Brian Salomon05441c42017-05-15 16:45:49 -04003424GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003425 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003426 SkRect ellipse = GrTest::TestSquare(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003427 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003428 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003429}
3430
Jim Van Verth64b85892019-06-17 12:01:46 -04003431GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
3432 do {
3433 SkScalar rotate = random->nextSScalar1() * 360.f;
3434 SkScalar translateX = random->nextSScalar1() * 1000.f;
3435 SkScalar translateY = random->nextSScalar1() * 1000.f;
3436 SkScalar scale;
3437 do {
3438 scale = random->nextSScalar1() * 100.f;
3439 } while (scale == 0);
3440 SkMatrix viewMatrix;
3441 viewMatrix.setRotate(rotate);
3442 viewMatrix.postTranslate(translateX, translateY);
3443 viewMatrix.postScale(scale, scale);
3444 SkRect rect = GrTest::TestRect(random);
3445 SkScalar radius = random->nextRangeF(0.1f, 10.f);
3446 SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
3447 if (rrect.isOval()) {
3448 continue;
3449 }
3450 std::unique_ptr<GrDrawOp> op =
3451 GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
3452 GrTest::TestStrokeRec(random), nullptr);
3453 if (op) {
3454 return op;
3455 }
3456 assert_alive(paint);
3457 } while (true);
3458}
3459
Brian Salomon05441c42017-05-15 16:45:49 -04003460GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003461 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003462 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Greg Daniel2655ede2019-04-10 00:49:28 +00003463 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003464 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003465}
3466
3467#endif