blob: 7d382e817d8243e44ceb5220ae2dc40a1d918dd0 [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
John Stiles8d9bf642020-08-12 15:07:45 -04001229#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04001230 SkString onDumpInfo() const override {
robertphillipse004bfc2015-11-16 09:06:59 -08001231 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001232 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001233 string.appendf(
1234 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1235 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001236 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001237 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1238 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1239 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001240 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001241 string += fHelper.dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001242 return string;
1243 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001244#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001245
Chris Dalton6ce447a2019-06-23 18:07:38 -06001246 GrProcessorSet::Analysis finalize(
1247 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1248 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001249 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001250 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001251 GrProcessorAnalysisCoverage::kSingleChannel, color,
1252 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001253 }
1254
1255 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1256
bsalomone46f9fe2015-08-18 06:05:14 -07001257private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001258 GrProgramInfo* programInfo() override { return fProgramInfo; }
Robert Phillips4490d922020-03-03 14:50:59 -05001259
Robert Phillips4133dc42020-03-11 15:55:55 -04001260 void onCreateProgramInfo(const GrCaps* caps,
1261 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001262 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001263 GrAppliedClip&& appliedClip,
1264 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001265 SkMatrix localMatrix;
1266 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001267 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001268 }
1269
Robert Phillips4490d922020-03-03 14:50:59 -05001270 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill, fClipPlane,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001271 fClipPlaneIsect, fClipPlaneUnion,
1272 fRoundCaps, fWideColor,
1273 localMatrix);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001274
Brian Salomon8afde5f2020-04-01 16:22:00 -04001275 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001276 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001277 }
1278
Robert Phillips4490d922020-03-03 14:50:59 -05001279 void onPrepareDraws(Target* target) override {
1280 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001281 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001282 if (!fProgramInfo) {
1283 return;
1284 }
1285 }
1286
Brian Salomon12d22642019-01-29 14:38:50 -05001287 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001288 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001289 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1290 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001291 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001292 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001293 return;
1294 }
1295
Brian Salomon12d22642019-01-29 14:38:50 -05001296 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001297 int firstIndex = 0;
1298 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1299 if (!indices) {
1300 SkDebugf("Could not allocate indices\n");
1301 return;
1302 }
1303
1304 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001305 for (const auto& circle : fCircles) {
1306 SkScalar innerRadius = circle.fInnerRadius;
1307 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001308 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001309 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001310
joshualitt76e7fb62015-02-11 08:52:27 -08001311 // The inner radius in the vertex data must be specified in normalized space.
1312 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001313 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001314
1315 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001316 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001317
Brian Osman9a24fee2018-08-03 09:48:42 -04001318 SkVector geoClipPlane = { 0, 0 };
1319 SkScalar offsetClipDist = SK_Scalar1;
1320 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1321 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1322 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1323 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1324 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1325 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1326 // the AA can extend just past the center of the circle.
1327 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1328 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1329 SkAssertResult(geoClipPlane.normalize());
1330 offsetClipDist = 0.5f / halfWidth;
1331 }
1332
Brian Osman7d8f82b2018-11-08 10:24:09 -05001333 for (int i = 0; i < 8; ++i) {
1334 // This clips the normalized offset to the half-plane we computed above. Then we
1335 // compute the vertex position from this.
Brian Osman788b9162020-02-07 10:36:46 -05001336 SkScalar dist = std::min(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
Brian Osman9d958b52018-11-13 12:46:56 -05001337 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001338 vertices.write(center + offset * halfWidth,
1339 color,
1340 offset,
1341 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001342 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001343 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001344 }
1345 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001346 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 }
1348 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
Brian Salomon45c92202018-04-10 10:53:58 -04001354 }
jvanverth6ca48822016-10-07 06:57:32 -07001355
Brian Salomon05441c42017-05-15 16:45:49 -04001356 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001357 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001358
Brian Osman7d8f82b2018-11-08 10:24:09 -05001359 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001360 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1361 color,
1362 kOctagonInner[i] * innerRadius,
1363 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001364 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001365 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001366 }
1367 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001369 }
1370 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001372 }
1373 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001374 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001375 }
Brian Salomon45c92202018-04-10 10:53:58 -04001376 }
jvanverth6ca48822016-10-07 06:57:32 -07001377 } else {
1378 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001379 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001380 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001381 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001382 }
jvanverth6ca48822016-10-07 06:57:32 -07001383 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001384 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001385 }
1386 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001387 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001388 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001389 if (fRoundCaps) {
1390 vertices.write(circle.fRoundCapCenters);
1391 }
jvanverth6ca48822016-10-07 06:57:32 -07001392 }
1393
Brian Salomon05441c42017-05-15 16:45:49 -04001394 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1395 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001396 for (int i = 0; i < primIndexCount; ++i) {
1397 *indices++ = primIndices[i] + currStartVertex;
1398 }
1399
Brian Salomon05441c42017-05-15 16:45:49 -04001400 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001401 }
jvanverth6ca48822016-10-07 06:57:32 -07001402
Robert Phillips4490d922020-03-03 14:50:59 -05001403 fMesh = target->allocMesh();
1404 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001405 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001406 }
1407
1408 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001409 if (!fProgramInfo || !fMesh) {
1410 return;
1411 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001412
Chris Dalton765ed362020-03-16 17:34:44 -06001413 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1414 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1415 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001416 }
1417
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001418 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1419 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001420 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001421
1422 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001423 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001424 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001425 }
1426
Brian Salomon05441c42017-05-15 16:45:49 -04001427 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001428 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001429 }
1430
Brian Salomon05441c42017-05-15 16:45:49 -04001431 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001432 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1433 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001434 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001435 }
1436
Brian Salomon289e3d82016-12-14 15:52:56 -05001437 // Because we've set up the ops that don't use the planes with noop values
1438 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001439 fClipPlane |= that->fClipPlane;
1440 fClipPlaneIsect |= that->fClipPlaneIsect;
1441 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001442 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001443 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001444
Brian Salomon05441c42017-05-15 16:45:49 -04001445 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001446 fVertCount += that->fVertCount;
1447 fIndexCount += that->fIndexCount;
1448 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001449 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001450 }
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
John Stiles8d9bf642020-08-12 15:07:45 -04001590#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04001591 SkString onDumpInfo() const override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001592 SkString string;
1593 for (int i = 0; i < fCircles.count(); ++i) {
1594 string.appendf(
1595 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1596 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1597 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001598 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001599 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1600 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1601 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1602 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001603 }
1604 string += fHelper.dumpInfo();
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001605 return string;
1606 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001607#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001608
Chris Dalton6ce447a2019-06-23 18:07:38 -06001609 GrProcessorSet::Analysis finalize(
1610 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1611 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001612 SkPMColor4f* color = &fCircles.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001613 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001614 GrProcessorAnalysisCoverage::kSingleChannel, color,
1615 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001616 }
1617
1618 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1619
1620private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001621 GrProgramInfo* programInfo() override { return fProgramInfo; }
1622
Robert Phillips4133dc42020-03-11 15:55:55 -04001623 void onCreateProgramInfo(const GrCaps* caps,
1624 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001625 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001626 GrAppliedClip&& appliedClip,
1627 const GrXferProcessor::DstProxyView& dstProxyView) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001628 SkMatrix localMatrix;
1629 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001630 return;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001631 }
1632
1633 // Setup geometry processor
Robert Phillips4490d922020-03-03 14:50:59 -05001634 GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(arena,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05001635 fWideColor,
1636 localMatrix);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001637
Brian Salomon8afde5f2020-04-01 16:22:00 -04001638 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001639 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001640 }
1641
Robert Phillips4490d922020-03-03 14:50:59 -05001642 void onPrepareDraws(Target* target) override {
1643 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001644 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001645 if (!fProgramInfo) {
1646 return;
1647 }
1648 }
1649
Brian Salomon12d22642019-01-29 14:38:50 -05001650 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001651 int firstVertex;
Robert Phillips4490d922020-03-03 14:50:59 -05001652 GrVertexWriter vertices{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
1653 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmanf9aabff2018-11-13 16:11:38 -05001654 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001655 SkDebugf("Could not allocate vertices\n");
1656 return;
1657 }
1658
Brian Salomon12d22642019-01-29 14:38:50 -05001659 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001660 int firstIndex = 0;
1661 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1662 if (!indices) {
1663 SkDebugf("Could not allocate indices\n");
1664 return;
1665 }
1666
1667 int currStartVertex = 0;
1668 for (const auto& circle : fCircles) {
1669 // The inner radius in the vertex data must be specified in normalized space so that
1670 // length() can be called with smaller values to avoid precision issues with half
1671 // floats.
1672 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1673 const SkRect& bounds = circle.fDevBounds;
1674 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001675 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1676 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1677 };
1678 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001679 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001680 dashParams.totalAngle = -dashParams.totalAngle;
1681 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001682 }
1683
Brian Osmane3caf2d2018-11-21 13:48:36 -05001684 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001685
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001686 // The bounding geometry for the circle is composed of an outer bounding octagon and
1687 // an inner bounded octagon.
1688
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001689 // Compute the vertices of the outer octagon.
1690 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1691 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001692
1693 auto reflectY = [=](const SkPoint& p) {
1694 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001695 };
Brian Osman9d958b52018-11-13 12:46:56 -05001696
1697 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001698 vertices.write(center + kOctagonOuter[i] * halfWidth,
1699 color,
1700 reflectY(kOctagonOuter[i]),
1701 circle.fOuterRadius,
1702 normInnerRadius,
1703 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001704 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001705
1706 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001707 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001708 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1709 color,
1710 reflectY(kOctagonInner[i]) * normInnerRadius,
1711 circle.fOuterRadius,
1712 normInnerRadius,
1713 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001714 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001715
1716 const uint16_t* primIndices = circle_type_to_indices(true);
1717 const int primIndexCount = circle_type_to_index_count(true);
1718 for (int i = 0; i < primIndexCount; ++i) {
1719 *indices++ = primIndices[i] + currStartVertex;
1720 }
1721
1722 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 }
1724
Robert Phillips4490d922020-03-03 14:50:59 -05001725 fMesh = target->allocMesh();
1726 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06001727 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001728 }
1729
1730 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05001731 if (!fProgramInfo || !fMesh) {
1732 return;
1733 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05001734
Chris Dalton765ed362020-03-16 17:34:44 -06001735 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
1736 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
1737 flushState->drawMesh(*fMesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001738 }
1739
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05001740 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
1741 const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001742 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1743
1744 // can only represent 65535 unique vertices with 16-bit indices
1745 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001746 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001747 }
1748
1749 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001750 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001751 }
1752
1753 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05001754 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
1755 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001756 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001757 }
1758
1759 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001760 fVertCount += that->fVertCount;
1761 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001762 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001763 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001764 }
1765
1766 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
John Stiles8d9bf642020-08-12 15:07:45 -04001920#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04001921 SkString onDumpInfo() const override {
1922 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001923 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001924 string.appendf(
1925 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1926 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001927 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001928 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1929 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001930 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001931 string += fHelper.dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001932 return string;
1933 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001934#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001935
Chris Dalton6ce447a2019-06-23 18:07:38 -06001936 GrProcessorSet::Analysis finalize(
1937 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
1938 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001939 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1940 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001941 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06001942 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001943 GrProcessorAnalysisCoverage::kSingleChannel, color,
1944 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001945 }
1946
1947 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1948
bsalomone46f9fe2015-08-18 06:05:14 -07001949private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04001950 GrProgramInfo* programInfo() override { return fProgramInfo; }
1951
Robert Phillips4133dc42020-03-11 15:55:55 -04001952 void onCreateProgramInfo(const GrCaps* caps,
1953 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04001954 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04001955 GrAppliedClip&& appliedClip,
1956 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001957 SkMatrix localMatrix;
1958 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001959 return;
joshualitt76e7fb62015-02-11 08:52:27 -08001960 }
1961
Robert Phillips4490d922020-03-03 14:50:59 -05001962 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
1963 fUseScale, localMatrix);
1964
Brian Salomon8afde5f2020-04-01 16:22:00 -04001965 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04001966 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05001967 }
1968
Robert Phillips4490d922020-03-03 14:50:59 -05001969 void onPrepareDraws(Target* target) override {
1970 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04001971 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05001972 if (!fProgramInfo) {
1973 return;
1974 }
1975 }
1976
1977 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001978 GrVertexWriter verts{helper.vertices()};
1979 if (!verts.fPtr) {
Robert Phillips4490d922020-03-03 14:50:59 -05001980 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001981 return;
1982 }
1983
Brian Salomon05441c42017-05-15 16:45:49 -04001984 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001985 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001986 SkScalar xRadius = ellipse.fXRadius;
1987 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001988
1989 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001990 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1991 SkScalarInvert(xRadius),
1992 SkScalarInvert(yRadius),
1993 SkScalarInvert(ellipse.fInnerXRadius),
1994 SkScalarInvert(ellipse.fInnerYRadius)
1995 };
Greg Daniel2655ede2019-04-10 00:49:28 +00001996 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1997 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
vjiaoblack977996d2016-06-30 12:20:54 -07001998
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001999 if (!fStroked) {
2000 // For filled ellipses we map a unit circle in the vertex attributes rather than
2001 // computing an ellipse and modifying that distance, so we normalize to 1
2002 xMaxOffset /= xRadius;
2003 yMaxOffset /= yRadius;
2004 }
2005
joshualitt76e7fb62015-02-11 08:52:27 -08002006 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05002007 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
2008 color,
2009 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Brian Osman788b9162020-02-07 10:36:46 -05002010 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002011 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002012 }
Robert Phillips4490d922020-03-03 14:50:59 -05002013 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002014 }
2015
2016 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002017 if (!fProgramInfo || !fMesh) {
2018 return;
2019 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002020
Chris Dalton765ed362020-03-16 17:34:44 -06002021 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2022 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2023 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002024 }
2025
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002026 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2027 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002028 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002029
Brian Salomon05441c42017-05-15 16:45:49 -04002030 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002031 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002032 }
2033
bsalomoncdaa97b2016-03-08 08:30:14 -08002034 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002035 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002036 }
2037
Brian Salomon05441c42017-05-15 16:45:49 -04002038 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002039 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2040 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002041 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002042 }
2043
Brian Salomon05441c42017-05-15 16:45:49 -04002044 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002045 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002046 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002047 }
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
John Stiles8d9bf642020-08-12 15:07:45 -04002195#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04002196 SkString onDumpInfo() const override {
Brian Salomon7c3e7182016-12-01 09:35:30 -05002197 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002198 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002199 string.appendf(
2200 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2201 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2202 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002203 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002204 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2205 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002206 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002207 string += fHelper.dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002208 return string;
2209 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002210#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002211
Chris Dalton6ce447a2019-06-23 18:07:38 -06002212 GrProcessorSet::Analysis finalize(
2213 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2214 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002215 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2216 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002217 SkPMColor4f* color = &fEllipses.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002218 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002219 GrProcessorAnalysisCoverage::kSingleChannel, color,
2220 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002221 }
2222
2223 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2224
bsalomone46f9fe2015-08-18 06:05:14 -07002225private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002226 GrProgramInfo* programInfo() override { return fProgramInfo; }
2227
Robert Phillips4133dc42020-03-11 15:55:55 -04002228 void onCreateProgramInfo(const GrCaps* caps,
2229 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002230 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002231 GrAppliedClip&& appliedClip,
2232 const GrXferProcessor::DstProxyView& dstProxyView) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002233 GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(arena, fWideColor, fUseScale,
2234 this->viewMatrix(),
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002235 this->style());
joshualitt76e7fb62015-02-11 08:52:27 -08002236
Brian Salomon8afde5f2020-04-01 16:22:00 -04002237 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002238 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002239 }
2240
Robert Phillips4490d922020-03-03 14:50:59 -05002241 void onPrepareDraws(Target* target) override {
2242 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002243 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002244 }
2245
2246 QuadHelper helper(target, fProgramInfo->primProc().vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002247 GrVertexWriter verts{helper.vertices()};
2248 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002249 return;
2250 }
2251
Brian Salomon05441c42017-05-15 16:45:49 -04002252 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002253 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002254 SkScalar xRadius = ellipse.fXRadius;
2255 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002256
joshualitt76e7fb62015-02-11 08:52:27 -08002257 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002258 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2259 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002260
Brian Osman9d958b52018-11-13 12:46:56 -05002261 // By default, constructed so that inner offset is (0, 0) for all points
2262 SkScalar innerRatioX = -offsetDx;
2263 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002264
Brian Osman9d958b52018-11-13 12:46:56 -05002265 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002266 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002267 innerRatioX = xRadius / ellipse.fInnerXRadius;
2268 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002269 }
joshualitt76e7fb62015-02-11 08:52:27 -08002270
Brian Osman2b6e3902018-11-21 15:29:43 -05002271 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2272 color,
2273 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Brian Osman788b9162020-02-07 10:36:46 -05002274 GrVertexWriter::If(fUseScale, std::max(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002275 origin_centered_tri_strip(innerRatioX + offsetDx,
2276 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002277 }
Robert Phillips4490d922020-03-03 14:50:59 -05002278 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002279 }
2280
2281 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002282 if (!fProgramInfo || !fMesh) {
2283 return;
2284 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002285
Chris Dalton765ed362020-03-16 17:34:44 -06002286 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2287 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2288 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002289 }
halcanary9d524f22016-03-29 09:03:52 -07002290
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002291 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2292 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002293 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002294 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002295 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002296 }
2297
bsalomoncdaa97b2016-03-08 08:30:14 -08002298 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002299 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002300 }
2301
joshualittd96a67b2015-05-05 14:09:05 -07002302 // TODO rewrite to allow positioning on CPU
Mike Reed2c383152019-12-18 16:47:47 -05002303 if (!SkMatrixPriv::CheapEqual(this->viewMatrix(), that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002304 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002305 }
2306
Brian Salomon05441c42017-05-15 16:45:49 -04002307 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002308 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002309 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002310 }
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
John Stiles8d9bf642020-08-12 15:07:45 -04002544#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04002545 SkString onDumpInfo() const override {
jvanverthc3d0e422016-08-25 08:12:35 -07002546 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002547 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002548 string.appendf(
2549 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2550 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002551 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002552 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2553 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2554 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002555 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002556 string += fHelper.dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002557 return string;
2558 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002559#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002560
Chris Dalton6ce447a2019-06-23 18:07:38 -06002561 GrProcessorSet::Analysis finalize(
2562 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2563 GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002564 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002565 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002566 GrProcessorAnalysisCoverage::kSingleChannel, color,
2567 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002568 }
2569
2570 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2571
Brian Salomon92aee3d2016-12-21 09:20:25 -05002572private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002573 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002574 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002575 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002576 SkASSERT(smInset < bigInset);
2577
2578 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002579 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2580 color,
2581 xOffset, 0.0f,
2582 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002583
2584 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002585 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2586 color,
2587 xOffset, 0.0f,
2588 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002589
Brian Osmana1d4eb92018-12-06 16:33:10 -05002590 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2591 color,
2592 0.0f, 0.0f,
2593 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002594
Brian Osmana1d4eb92018-12-06 16:33:10 -05002595 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2596 color,
2597 0.0f, 0.0f,
2598 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002599
Brian Osmana1d4eb92018-12-06 16:33:10 -05002600 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2601 color,
2602 0.0f, 0.0f,
2603 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002604
Brian Osmana1d4eb92018-12-06 16:33:10 -05002605 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2606 color,
2607 0.0f, 0.0f,
2608 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002609
2610 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002611 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2612 color,
2613 xOffset, 0.0f,
2614 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002615
2616 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002617 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2618 color,
2619 xOffset, 0.0f,
2620 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002621 }
2622
Robert Phillips2669a7b2020-03-12 12:07:19 -04002623 GrProgramInfo* programInfo() override { return fProgramInfo; }
2624
Robert Phillips4133dc42020-03-11 15:55:55 -04002625 void onCreateProgramInfo(const GrCaps* caps,
2626 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002627 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002628 GrAppliedClip&& appliedClip,
2629 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002630 // Invert the view matrix as a local matrix (if any other processors require coords).
2631 SkMatrix localMatrix;
2632 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002633 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002634 }
2635
Robert Phillips4490d922020-03-03 14:50:59 -05002636 GrGeometryProcessor* gp = CircleGeometryProcessor::Make(arena, !fAllFill,
Robert Phillips7cd0bfe2019-11-20 16:08:10 -05002637 false, false, false, false,
2638 fWideColor, localMatrix);
joshualitt76e7fb62015-02-11 08:52:27 -08002639
Brian Salomon8afde5f2020-04-01 16:22:00 -04002640 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002641 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002642 }
2643
Robert Phillips4490d922020-03-03 14:50:59 -05002644 void onPrepareDraws(Target* target) override {
2645 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002646 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002647 if (!fProgramInfo) {
2648 return;
2649 }
2650 }
2651
Brian Salomon12d22642019-01-29 14:38:50 -05002652 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002653 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002654
Robert Phillips4490d922020-03-03 14:50:59 -05002655 GrVertexWriter verts{target->makeVertexSpace(fProgramInfo->primProc().vertexStride(),
2656 fVertCount, &vertexBuffer, &firstVertex)};
Brian Osmana1d4eb92018-12-06 16:33:10 -05002657 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002658 SkDebugf("Could not allocate vertices\n");
2659 return;
2660 }
2661
Brian Salomon12d22642019-01-29 14:38:50 -05002662 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002663 int firstIndex = 0;
2664 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2665 if (!indices) {
2666 SkDebugf("Could not allocate indices\n");
2667 return;
2668 }
2669
2670 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002671 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002672 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002673 SkScalar outerRadius = rrect.fOuterRadius;
2674 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002675
Brian Salomon289e3d82016-12-14 15:52:56 -05002676 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2677 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002678
Brian Salomon289e3d82016-12-14 15:52:56 -05002679 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002680 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002681 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002682 SkScalar innerRadius = rrect.fType != kFill_RRectType
2683 ? rrect.fInnerRadius / rrect.fOuterRadius
2684 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002685 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002686 verts.write(bounds.fLeft, yCoords[i],
2687 color,
2688 -1.0f, yOuterRadii[i],
2689 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002690
Brian Osmana1d4eb92018-12-06 16:33:10 -05002691 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2692 color,
2693 0.0f, yOuterRadii[i],
2694 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002695
Brian Osmana1d4eb92018-12-06 16:33:10 -05002696 verts.write(bounds.fRight - outerRadius, yCoords[i],
2697 color,
2698 0.0f, yOuterRadii[i],
2699 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002700
Brian Osmana1d4eb92018-12-06 16:33:10 -05002701 verts.write(bounds.fRight, yCoords[i],
2702 color,
2703 1.0f, yOuterRadii[i],
2704 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002705 }
jvanverthc3d0e422016-08-25 08:12:35 -07002706 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002707 // Effectively this is an additional stroked rrect, with its
2708 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2709 // This will give us correct AA in the center and the correct
2710 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002711 //
jvanvertha4f1af82016-08-29 07:17:47 -07002712 // Also, the outer offset is a constant vector pointing to the right, which
2713 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002714 if (kOverstroke_RRectType == rrect.fType) {
2715 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002716
Brian Salomon05441c42017-05-15 16:45:49 -04002717 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002718 // this is the normalized distance from the outer rectangle of this
2719 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002720 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002721
Brian Osmana1d4eb92018-12-06 16:33:10 -05002722 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002723 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002724 }
jvanverth6a397612016-08-26 08:15:33 -07002725
Brian Salomon05441c42017-05-15 16:45:49 -04002726 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2727 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002728 for (int i = 0; i < primIndexCount; ++i) {
2729 *indices++ = primIndices[i] + currStartVertex;
2730 }
2731
Brian Salomon05441c42017-05-15 16:45:49 -04002732 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002733 }
2734
Robert Phillips4490d922020-03-03 14:50:59 -05002735 fMesh = target->allocMesh();
2736 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Chris Dalton37c7bdd2020-03-13 09:21:12 -06002737 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002738 }
2739
2740 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05002741 if (!fProgramInfo || !fMesh) {
2742 return;
2743 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05002744
Chris Dalton765ed362020-03-16 17:34:44 -06002745 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
2746 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
2747 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002748 }
2749
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05002750 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
2751 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002752 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002753
2754 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002755 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002756 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002757 }
2758
Brian Salomon05441c42017-05-15 16:45:49 -04002759 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002760 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002761 }
2762
Brian Salomon05441c42017-05-15 16:45:49 -04002763 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05002764 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
2765 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002766 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002767 }
2768
Brian Salomon05441c42017-05-15 16:45:49 -04002769 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002770 fVertCount += that->fVertCount;
2771 fIndexCount += that->fIndexCount;
2772 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002773 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002774 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002775 }
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 Osman9fb7fa52019-07-01 16:48:39 -04002839 SkASSERT(devXRadius >= 0.5);
2840 SkASSERT(devYRadius >= 0.5);
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
John Stiles8d9bf642020-08-12 15:07:45 -04002915#if GR_TEST_UTILS
John Stiles8dd1e222020-08-12 19:06:24 -04002916 SkString onDumpInfo() const override {
2917 SkString string = SkStringPrintf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002918 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002919 string.appendf(
2920 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2921 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002922 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002923 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2924 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002925 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002926 string += fHelper.dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002927 return string;
2928 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002929#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002930
Chris Dalton6ce447a2019-06-23 18:07:38 -06002931 GrProcessorSet::Analysis finalize(
2932 const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
2933 GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002934 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002935 SkPMColor4f* color = &fRRects.front().fColor;
Chris Dalton6ce447a2019-06-23 18:07:38 -06002936 return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002937 GrProcessorAnalysisCoverage::kSingleChannel, color,
2938 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002939 }
2940
2941 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2942
bsalomone46f9fe2015-08-18 06:05:14 -07002943private:
Robert Phillips2669a7b2020-03-12 12:07:19 -04002944 GrProgramInfo* programInfo() override { return fProgramInfo; }
2945
Robert Phillips4133dc42020-03-11 15:55:55 -04002946 void onCreateProgramInfo(const GrCaps* caps,
2947 SkArenaAlloc* arena,
Brian Salomon8afde5f2020-04-01 16:22:00 -04002948 const GrSurfaceProxyView* writeView,
Robert Phillips4133dc42020-03-11 15:55:55 -04002949 GrAppliedClip&& appliedClip,
2950 const GrXferProcessor::DstProxyView& dstProxyView) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002951 SkMatrix localMatrix;
2952 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002953 return;
joshualitt76e7fb62015-02-11 08:52:27 -08002954 }
2955
Robert Phillips4490d922020-03-03 14:50:59 -05002956 GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(arena, fStroked, fWideColor,
2957 fUseScale, localMatrix);
2958
Brian Salomon8afde5f2020-04-01 16:22:00 -04002959 fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, std::move(appliedClip),
Robert Phillips4133dc42020-03-11 15:55:55 -04002960 dstProxyView, gp, GrPrimitiveType::kTriangles);
Robert Phillips4490d922020-03-03 14:50:59 -05002961 }
2962
Robert Phillips4490d922020-03-03 14:50:59 -05002963 void onPrepareDraws(Target* target) override {
2964 if (!fProgramInfo) {
Robert Phillips4133dc42020-03-11 15:55:55 -04002965 this->createProgramInfo(target);
Robert Phillips4490d922020-03-03 14:50:59 -05002966 if (!fProgramInfo) {
2967 return;
2968 }
2969 }
joshualitt76e7fb62015-02-11 08:52:27 -08002970
bsalomonb5238a72015-05-05 07:49:49 -07002971 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002972 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002973 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2974 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002975
Brian Salomon12d22642019-01-29 14:38:50 -05002976 if (!indexBuffer) {
2977 SkDebugf("Could not allocate indices\n");
2978 return;
2979 }
Robert Phillips4490d922020-03-03 14:50:59 -05002980 PatternHelper helper(target, GrPrimitiveType::kTriangles,
2981 fProgramInfo->primProc().vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002982 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Robert Phillipsee08d522019-10-28 16:34:44 -04002983 fRRects.count(), kNumRRectsInIndexBuffer);
Brian Osmana1d4eb92018-12-06 16:33:10 -05002984 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002985 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002986 SkDebugf("Could not allocate vertices\n");
2987 return;
2988 }
2989
Brian Salomon05441c42017-05-15 16:45:49 -04002990 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002991 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002992 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002993 float reciprocalRadii[4] = {
2994 SkScalarInvert(rrect.fXRadius),
2995 SkScalarInvert(rrect.fYRadius),
2996 SkScalarInvert(rrect.fInnerXRadius),
2997 SkScalarInvert(rrect.fInnerYRadius)
2998 };
joshualitt76e7fb62015-02-11 08:52:27 -08002999
3000 // Extend the radii out half a pixel to antialias.
Greg Daniel2655ede2019-04-10 00:49:28 +00003001 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
3002 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08003003
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003004 SkScalar xMaxOffset = xOuterRadius;
3005 SkScalar yMaxOffset = yOuterRadius;
3006 if (!fStroked) {
3007 // For filled rrects we map a unit circle in the vertex attributes rather than
3008 // computing an ellipse and modifying that distance, so we normalize to 1.
3009 xMaxOffset /= rrect.fXRadius;
3010 yMaxOffset /= rrect.fYRadius;
3011 }
3012
Brian Salomon05441c42017-05-15 16:45:49 -04003013 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08003014
Brian Salomon289e3d82016-12-14 15:52:56 -05003015 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
3016 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003017 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05003018 SK_ScalarNearlyZero, // we're using inversesqrt() in
3019 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04003020 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08003021
Brian Osman788b9162020-02-07 10:36:46 -05003022 auto maybeScale = GrVertexWriter::If(fUseScale, std::max(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08003023 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05003024 verts.write(bounds.fLeft, yCoords[i],
3025 color,
3026 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003027 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003028 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003029
Brian Osmana1d4eb92018-12-06 16:33:10 -05003030 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
3031 color,
3032 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003033 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003034 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003035
Brian Osmana1d4eb92018-12-06 16:33:10 -05003036 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
3037 color,
3038 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003039 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003040 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003041
Brian Osmana1d4eb92018-12-06 16:33:10 -05003042 verts.write(bounds.fRight, yCoords[i],
3043 color,
3044 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04003045 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05003046 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08003047 }
3048 }
Robert Phillips4490d922020-03-03 14:50:59 -05003049 fMesh = helper.mesh();
Chris Dalton07cdcfc92019-02-26 11:13:22 -07003050 }
3051
3052 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
Robert Phillips4490d922020-03-03 14:50:59 -05003053 if (!fProgramInfo || !fMesh) {
3054 return;
3055 }
Robert Phillips3968fcb2019-12-05 16:40:31 -05003056
Chris Dalton765ed362020-03-16 17:34:44 -06003057 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
3058 flushState->bindTextures(fProgramInfo->primProc(), nullptr, fProgramInfo->pipeline());
3059 flushState->drawMesh(*fMesh);
joshualitt76e7fb62015-02-11 08:52:27 -08003060 }
3061
Michael Ludwig28b0c5d2019-12-19 14:51:00 -05003062 CombineResult onCombineIfPossible(GrOp* t, GrRecordingContext::Arenas*,
3063 const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05003064 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07003065
Brian Salomon05441c42017-05-15 16:45:49 -04003066 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003067 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07003068 }
3069
bsalomoncdaa97b2016-03-08 08:30:14 -08003070 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003071 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003072 }
3073
Brian Salomon05441c42017-05-15 16:45:49 -04003074 if (fHelper.usesLocalCoords() &&
Mike Reed2c383152019-12-18 16:47:47 -05003075 !SkMatrixPriv::CheapEqual(fViewMatrixIfUsingLocalCoords,
3076 that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00003077 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08003078 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003079
Brian Salomon05441c42017-05-15 16:45:49 -04003080 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05003081 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00003082 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08003083 }
3084
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