blob: 730c0fccdd2568e5ee7259481d0fafeb8fa25eb7 [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
Brian Salomon289e3d82016-12-14 15:52:56 -05008#include "GrOvalOpFactory.h"
Jim Van Verth20ae25c2019-03-29 08:50:41 -04009#include "GrCaps.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050010#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080011#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050012#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080013#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070014#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050015#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
Brian Osmanf9aabff2018-11-13 16:11:38 -050017#include "GrVertexWriter.h"
Mike Reed242135a2018-02-22 13:41:39 -050018#include "SkRRectPriv.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000019#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080023#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080024#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050025#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060026#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050027#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040028#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080029
Ben Wagnerf08d1d02018-06-18 15:11:00 -040030#include <utility>
31
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080033
Brian Salomon289e3d82016-12-14 15:52:56 -050034static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
Brian Osmane3caf2d2018-11-21 13:48:36 -050035
Brian Osman2b6e3902018-11-21 15:29:43 -050036// Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
37static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
38 return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
39};
40
commit-bot@chromium.org81312832013-03-22 18:34:09 +000041}
42
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000043///////////////////////////////////////////////////////////////////////////////
44
45/**
bsalomonce1c8862014-12-15 07:11:22 -080046 * The output of this effect is a modulation of the input color and coverage for a circle. It
47 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080048 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080049 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080050 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080051 * vec4f : (p.xy, outerRad, innerRad)
52 * p is the position in the normalized space.
53 * outerRad is the outerRadius in device space.
54 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070055 * Additional clip planes are supported for rendering circular arcs. The additional planes are
56 * either intersected or unioned together. Up to three planes are supported (an initial plane,
57 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050058 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070059 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040060 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
61 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000062 */
63
bsalomoncdaa97b2016-03-08 08:30:14 -080064class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000065public:
Jim Van Verth10bf7022019-04-09 15:55:05 -040066 CircleGeometryProcessor(bool aa, bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Osmane3caf2d2018-11-21 13:48:36 -050067 bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040068 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040069 , fLocalMatrix(localMatrix)
Jim Van Verth10bf7022019-04-09 15:55:05 -040070 , fAA(aa)
Brian Salomon45c92202018-04-10 10:53:58 -040071 , fStroke(stroke) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -050072 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -050073 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -050074 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
75
bsalomon4f3a0ca2016-08-22 13:14:26 -070076 if (clipPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040077 fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 }
79 if (isectPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040080 fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070081 }
82 if (unionPlane) {
Brian Osmand4c29702018-09-14 16:16:55 -040083 fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
bsalomon4f3a0ca2016-08-22 13:14:26 -070084 }
Brian Salomon45c92202018-04-10 10:53:58 -040085 if (roundCaps) {
86 SkASSERT(stroke);
87 SkASSERT(clipPlane);
Brian Osmand4c29702018-09-14 16:16:55 -040088 fInRoundCapCenters =
89 {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Salomon45c92202018-04-10 10:53:58 -040090 }
Brian Osmanf04fb3c2018-11-12 15:34:00 -050091 this->setVertexAttributes(&fInPosition, 7);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092 }
93
Brian Salomond3b65972017-03-22 12:05:03 -040094 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095
mtklein36352bf2015-03-25 18:17:31 -070096 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
Brian Salomon94efbf52016-11-29 13:43:05 -050098 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -070099 GLSLProcessor::GenKey(*this, caps, b);
100 }
101
Brian Salomon94efbf52016-11-29 13:43:05 -0500102 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700103 return new GLSLProcessor();
104 }
105
106private:
egdaniel57d3b032015-11-13 11:57:27 -0800107 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000108 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800109 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110
Brian Salomon289e3d82016-12-14 15:52:56 -0500111 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800112 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800113 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800114 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800115 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700116 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800117
joshualittabb52a12015-01-13 15:02:10 -0800118 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800119 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400120 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500121 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
Brian Salomon92be2f72018-06-19 14:33:47 -0400122 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400123 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700124 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
125 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400126 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400127 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700128 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
129 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400130 if (cgp.fInUnionPlane.isInitialized()) {
131 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400132 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700133 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
134 }
Brian Salomon45c92202018-04-10 10:53:58 -0400135 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400136 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400137 fragBuilder->codeAppend("float4 roundCapCenters;");
138 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
139 varyingHandler->addVarying("capRadius", &capRadius,
140 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
141 // This is the cap radius in normalized space where the outer radius is 1 and
142 // circledEdge.w is the normalized inner radius.
143 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500144 cgp.fInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400145 }
joshualittabb52a12015-01-13 15:02:10 -0800146
joshualittb8c241a2015-05-19 08:23:30 -0700147 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500148 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800149
joshualittabb52a12015-01-13 15:02:10 -0800150 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500151 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800152
153 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800154 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800155 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800156 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500157 cgp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700158 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700159 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800160
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400161 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500162 fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
Jim Van Verth10bf7022019-04-09 15:55:05 -0400163 if (cgp.fAA) {
164 fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
165 } else {
166 fragBuilder->codeAppend("half edgeAlpha = step(0, distanceToOuterEdge);");
167 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800168 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500169 fragBuilder->codeAppend(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500170 "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
Jim Van Verth10bf7022019-04-09 15:55:05 -0400171 if (cgp.fAA) {
172 fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
173 } else {
174 fragBuilder->codeAppend("half innerAlpha = step(0, distanceToInnerEdge);");
175 }
egdaniel4ca2e602015-11-18 08:01:26 -0800176 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000177 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000178
Brian Salomon92be2f72018-06-19 14:33:47 -0400179 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500180 fragBuilder->codeAppend(
Jim Van Verth10bf7022019-04-09 15:55:05 -0400181 "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
182 "clipPlane.xy) + clipPlane.z));");
Brian Salomon92be2f72018-06-19 14:33:47 -0400183 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500184 fragBuilder->codeAppend(
Jim Van Verth10bf7022019-04-09 15:55:05 -0400185 "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
186 "isectPlane.xy) + isectPlane.z));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400188 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500189 fragBuilder->codeAppend(
Jim Van Verth10bf7022019-04-09 15:55:05 -0400190 "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
191 " unionPlane.xy) + unionPlane.z)));");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700192 }
193 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400194 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400195 // We compute coverage of the round caps as circles at the butt caps produced
196 // by the clip planes. The inverse of the clip planes is applied so that there
197 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400198 fragBuilder->codeAppendf(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500199 "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
200 " roundCapCenters.xy)));"
201 "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
202 " roundCapCenters.zw)));"
Brian Salomon33c44222018-04-19 08:44:21 -0400203 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400204 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
205 capRadius.fsIn(), capRadius.fsIn());
206 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700207 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000208 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000209 }
210
robertphillips46d36f02015-01-18 08:14:14 -0800211 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500212 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700213 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800214 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700215 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500216 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700217 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon92be2f72018-06-19 14:33:47 -0400218 key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
219 key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
220 key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
221 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
Jim Van Verth10bf7022019-04-09 15:55:05 -0400222 key |= cgp.fAA ? 0x40 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700223 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224 }
225
bsalomona624bf32016-09-20 09:12:47 -0700226 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
227 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700228 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700229 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700230 }
231
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232 private:
egdaniele659a582015-11-13 09:55:43 -0800233 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234 };
235
Brian Salomon289e3d82016-12-14 15:52:56 -0500236 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400237
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500238 Attribute fInPosition;
239 Attribute fInColor;
240 Attribute fInCircleEdge;
Brian Salomon92be2f72018-06-19 14:33:47 -0400241 // Optional attributes.
242 Attribute fInClipPlane;
243 Attribute fInIsectPlane;
244 Attribute fInUnionPlane;
245 Attribute fInRoundCapCenters;
246
Jim Van Verth10bf7022019-04-09 15:55:05 -0400247 bool fAA;
Brian Salomon289e3d82016-12-14 15:52:56 -0500248 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400249 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250
joshualitt249af152014-09-15 11:41:13 -0700251 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252};
253
bsalomoncdaa97b2016-03-08 08:30:14 -0800254GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000255
Hal Canary6f6961e2017-01-31 13:50:44 -0500256#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700257sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Jim Van Verth10bf7022019-04-09 15:55:05 -0400258 bool aa = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400259 bool stroke = d->fRandom->nextBool();
260 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
Brian Osmane3caf2d2018-11-21 13:48:36 -0500261 bool wideColor = d->fRandom->nextBool();
Brian Salomon45c92202018-04-10 10:53:58 -0400262 bool clipPlane = d->fRandom->nextBool();
263 bool isectPlane = d->fRandom->nextBool();
264 bool unionPlane = d->fRandom->nextBool();
265 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Brian Osmane3caf2d2018-11-21 13:48:36 -0500266 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
Jim Van Verth10bf7022019-04-09 15:55:05 -0400267 aa, stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268}
Hal Canary6f6961e2017-01-31 13:50:44 -0500269#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000270
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400271class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
272public:
Brian Osmane3caf2d2018-11-21 13:48:36 -0500273 ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500275 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500276 fInColor = MakeColorAttribute("inColor", wideColor);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500277 fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
278 fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
279 this->setVertexAttributes(&fInPosition, 4);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400280 }
281
282 ~ButtCapDashedCircleGeometryProcessor() override {}
283
284 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
285
286 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
287 GLSLProcessor::GenKey(*this, caps, b);
288 }
289
290 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
291 return new GLSLProcessor();
292 }
293
294private:
295 class GLSLProcessor : public GrGLSLGeometryProcessor {
296 public:
297 GLSLProcessor() {}
298
299 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
300 const ButtCapDashedCircleGeometryProcessor& bcscgp =
301 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
302 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
303 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
304 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
305 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
306
307 // emit attributes
308 varyingHandler->emitAttributes(bcscgp);
309 fragBuilder->codeAppend("float4 circleEdge;");
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500310 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400311
312 fragBuilder->codeAppend("float4 dashParams;");
313 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500314 bcscgp.fInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400315 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
316 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
317 varyingHandler->addVarying("wrapDashes", &wrapDashes,
318 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
319 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
320 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
321 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500322 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400323 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
324 // x = length of on interval, y = length of on + off.
325 // There are two other parameters in dashParams.zw:
326 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
327 // Each interval has a "corresponding" dash which may be shifted partially or
328 // fully out of its interval by the phase. So there may be up to two "visual"
329 // dashes in an interval.
330 // When computing coverage in an interval we look at three dashes. These are the
331 // "corresponding" dashes from the current, previous, and next intervals. Any of these
332 // may be phase shifted into our interval or even when phase=0 they may be within half a
333 // pixel distance of a pixel center in the interval.
334 // When in the first interval we need to check the dash from the last interval. And
335 // similarly when in the last interval we need to check the dash from the first
336 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
337 // We compute the dash begin/end angles in the vertex shader and apply them in the
338 // fragment shader when we detect we're in the first/last interval.
339 vertBuilder->codeAppend(R"(
340 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
341 // to the fragment shader as a varying.
342 float4 wrapDashes;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500343 half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400344 // We can happen to be perfectly divisible.
345 if (0 == lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500346 lastIntervalLength = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400347 }
348 // Let 'l' be the last interval before reaching 2 pi.
349 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
350 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
351 // interval.
352 half offset = 0;
353 if (-dashParams.w >= lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500354 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400355 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500356 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400357 }
358 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
359 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
360 // min.
361 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
362
363 // Based on the phase determine whether the -1st, 0th, or 1st interval's
364 // "corresponding" dash appears in the 0th interval and is closest to l.
365 offset = 0;
366 if (dashParams.w >= dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500367 offset = half(dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400368 } else if (-dashParams.w > dashParams.y - dashParams.x) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500369 offset = half(-dashParams.y);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400370 }
371 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
372 wrapDashes.w = wrapDashes.z + dashParams.x;
373 // The start of the dash we're considering may be clipped by the start of the
374 // circle.
375 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
376 )");
Ethan Nicholase1f55022019-02-05 17:17:40 -0500377 vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400378 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
379 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
380 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
381
382 // setup pass through color
383 varyingHandler->addPassThroughAttribute(
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500384 bcscgp.fInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400385 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
386
387 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500388 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400389
390 // emit transforms
391 this->emitTransforms(vertBuilder,
392 varyingHandler,
393 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500394 bcscgp.fInPosition.asShaderVar(),
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400395 bcscgp.fLocalMatrix,
396 args.fFPCoordTransformHandler);
397 GrShaderVar fnArgs[] = {
398 GrShaderVar("angleToEdge", kFloat_GrSLType),
399 GrShaderVar("diameter", kFloat_GrSLType),
400 };
401 SkString fnName;
402 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
403 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
404 float linearDist;
405 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
406 linearDist = diameter * sin(angleToEdge / 2);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400407 return saturate(linearDist + 0.5);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400408 )",
409 &fnName);
410 fragBuilder->codeAppend(R"(
411 float d = length(circleEdge.xy) * circleEdge.z;
412
413 // Compute coverage from outer/inner edges of the stroke.
Ethan Nicholase1f55022019-02-05 17:17:40 -0500414 half distanceToOuterEdge = half(circleEdge.z - d);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400415 half edgeAlpha = saturate(distanceToOuterEdge);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500416 half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400417 half innerAlpha = saturate(distanceToInnerEdge);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400418 edgeAlpha *= innerAlpha;
419
Ethan Nicholase1f55022019-02-05 17:17:40 -0500420 half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400421 angleFromStart = mod(angleFromStart, 6.28318530718);
422 float x = mod(angleFromStart, dashParams.y);
423 // Convert the radial distance from center to pixel into a diameter.
424 d *= 2;
Ethan Nicholase1f55022019-02-05 17:17:40 -0500425 half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
426 half(dashParams.w));
427 half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
428 half(dashParams.y) + half(dashParams.x) -
429 half(dashParams.w));
430 half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
431 half(-dashParams.y) + half(dashParams.x) -
432 half(dashParams.w));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400433 half dashAlpha = 0;
434 )");
435 fragBuilder->codeAppendf(R"(
436 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500437 dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400438 currDash.y = min(currDash.y, lastIntervalLength);
439 if (nextDash.x >= lastIntervalLength) {
440 // The next dash is outside the 0..2pi range, throw it away
441 nextDash.xy = half2(1000);
442 } else {
443 // Clip the end of the next dash to the end of the circle
444 nextDash.y = min(nextDash.y, lastIntervalLength);
445 }
446 }
447 )", fnName.c_str(), fnName.c_str());
448 fragBuilder->codeAppendf(R"(
449 if (angleFromStart - x - dashParams.y < -0.01) {
Ethan Nicholase1f55022019-02-05 17:17:40 -0500450 dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400451 currDash.x = max(currDash.x, 0);
452 if (prevDash.y <= 0) {
453 // The previous dash is outside the 0..2pi range, throw it away
454 prevDash.xy = half2(1000);
455 } else {
456 // Clip the start previous dash to the start of the circle
457 prevDash.x = max(prevDash.x, 0);
458 }
459 }
460 )", fnName.c_str(), fnName.c_str());
461 fragBuilder->codeAppendf(R"(
Ethan Nicholase1f55022019-02-05 17:17:40 -0500462 dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
463 dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
464 dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400465 dashAlpha = min(dashAlpha, 1);
466 edgeAlpha *= dashAlpha;
467 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
468 fnName.c_str());
469 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
470 }
471
472 static void GenKey(const GrGeometryProcessor& gp,
473 const GrShaderCaps&,
474 GrProcessorKeyBuilder* b) {
475 const ButtCapDashedCircleGeometryProcessor& bcscgp =
476 gp.cast<ButtCapDashedCircleGeometryProcessor>();
477 b->add32(bcscgp.fLocalMatrix.hasPerspective());
478 }
479
480 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
481 FPCoordTransformIter&& transformIter) override {
482 this->setTransformDataHelper(
483 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
484 &transformIter);
485 }
486
487 private:
488 typedef GrGLSLGeometryProcessor INHERITED;
489 };
490
491 SkMatrix fLocalMatrix;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500492 Attribute fInPosition;
493 Attribute fInColor;
494 Attribute fInCircleEdge;
495 Attribute fInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400496
497 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
498
499 typedef GrGeometryProcessor INHERITED;
500};
501
502#if GR_TEST_UTILS
503sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Osmane3caf2d2018-11-21 13:48:36 -0500504 bool wideColor = d->fRandom->nextBool();
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400505 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
Brian Osmane3caf2d2018-11-21 13:48:36 -0500506 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(wideColor, matrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400507}
508#endif
509
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000510///////////////////////////////////////////////////////////////////////////////
511
512/**
513 * 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 +0000514 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
515 * in both x and y directions.
516 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000517 * 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 +0000518 */
519
bsalomoncdaa97b2016-03-08 08:30:14 -0800520class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000521public:
Jim Van Verth10bf7022019-04-09 15:55:05 -0400522 EllipseGeometryProcessor(bool aa, bool stroke, bool wideColor, bool useScale,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400523 const SkMatrix& localMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000524 : INHERITED(kEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400525 , fLocalMatrix(localMatrix)
Jim Van Verth10bf7022019-04-09 15:55:05 -0400526 , fAA(aa)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400527 , fStroke(stroke)
528 , fUseScale(useScale) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500529 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500530 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400531 if (useScale) {
532 fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
533 } else {
534 fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
535 }
536 fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500537 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000538 }
539
Brian Salomond3b65972017-03-22 12:05:03 -0400540 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000541
mtklein36352bf2015-03-25 18:17:31 -0700542 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800543
Brian Salomon94efbf52016-11-29 13:43:05 -0500544 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700545 GLSLProcessor::GenKey(*this, caps, b);
546 }
547
Brian Salomon94efbf52016-11-29 13:43:05 -0500548 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700549 return new GLSLProcessor();
550 }
551
552private:
egdaniel57d3b032015-11-13 11:57:27 -0800553 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000554 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800555 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000556
Brian Salomon289e3d82016-12-14 15:52:56 -0500557 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800558 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800559 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800560 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800561 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562
joshualittabb52a12015-01-13 15:02:10 -0800563 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800564 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800565
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400566 GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
567 GrGLSLVarying ellipseOffsets(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800568 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800569 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500570 egp.fInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000571
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400572 GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800573 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500574 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800575
Chris Dalton60283612018-02-14 13:38:14 -0700576 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700577 // setup pass through color
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500578 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800579
joshualittabb52a12015-01-13 15:02:10 -0800580 // Setup position
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500581 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800582
583 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800584 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800585 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800586 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500587 egp.fInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700588 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700589 args.fFPCoordTransformHandler);
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400590 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
591 // to compute both the edges because we need two separate test equations for
592 // the single offset.
593 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
594 // the distance by the gradient, non-uniformly scaled by the inverse of the
595 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800596
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400597 // On medium precision devices, we scale the denominator of the distance equation
598 // before taking the inverse square root to minimize the chance that we're dividing
599 // by zero, then we scale the result back.
600
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000601 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400602 fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400603 if (egp.fStroke) {
604 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
605 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400606 fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
607 if (egp.fUseScale) {
608 fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
609 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
610 } else {
611 fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
612 }
613 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700614
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000615 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400616 if (args.fShaderCaps->floatIs32Bits()) {
617 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
618 } else {
619 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
620 }
621 if (egp.fUseScale) {
622 fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
623 ellipseOffsets.fsIn());
624 } else {
625 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
626 }
Jim Van Verth10bf7022019-04-09 15:55:05 -0400627 if (egp.fAA) {
628 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
629 } else {
630 fragBuilder->codeAppend("float edgeAlpha = step(0, -test*invlen);");
631 }
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 }
Jim Van Verth10bf7022019-04-09 15:55:05 -0400654 if (egp.fAA) {
655 fragBuilder->codeAppend("float innerEdgeAlpha = saturate(0.5+test*invlen);");
656 } else {
657 fragBuilder->codeAppend("float innerEdgeAlpha = step(0, test*invlen);");
658 }
659 fragBuilder->codeAppend("edgeAlpha *= innerEdgeAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660 }
661
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400662 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663 }
664
robertphillips46d36f02015-01-18 08:14:14 -0800665 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500666 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700667 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800668 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
669 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700670 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
Jim Van Verth10bf7022019-04-09 15:55:05 -0400671 key |= egp.fAA ? 0x4 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700672 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000673 }
674
bsalomona624bf32016-09-20 09:12:47 -0700675 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
676 FPCoordTransformIter&& transformIter) override {
677 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
678 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700679 }
680
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000681 private:
egdaniele659a582015-11-13 09:55:43 -0800682 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000683 };
684
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500685 Attribute fInPosition;
686 Attribute fInColor;
687 Attribute fInEllipseOffset;
688 Attribute fInEllipseRadii;
Brian Salomon92be2f72018-06-19 14:33:47 -0400689
joshualitte3ababe2015-05-15 07:56:07 -0700690 SkMatrix fLocalMatrix;
Jim Van Verth10bf7022019-04-09 15:55:05 -0400691 bool fAA;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000692 bool fStroke;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400693 bool fUseScale;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000694
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400695 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000696
joshualitt249af152014-09-15 11:41:13 -0700697 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000698};
699
bsalomoncdaa97b2016-03-08 08:30:14 -0800700GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000701
Hal Canary6f6961e2017-01-31 13:50:44 -0500702#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700703sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000704 return sk_sp<GrGeometryProcessor>(
Brian Osmane3caf2d2018-11-21 13:48:36 -0500705 new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
Jim Van Verth10bf7022019-04-09 15:55:05 -0400706 d->fRandom->nextBool(), d->fRandom->nextBool(),
707 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000708}
Hal Canary6f6961e2017-01-31 13:50:44 -0500709#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000710
711///////////////////////////////////////////////////////////////////////////////
712
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000713/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000714 * 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 +0000715 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
716 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
717 * using differentials.
718 *
719 * The result is device-independent and can be used with any affine matrix.
720 */
721
bsalomoncdaa97b2016-03-08 08:30:14 -0800722enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000723
bsalomoncdaa97b2016-03-08 08:30:14 -0800724class DIEllipseGeometryProcessor : public GrGeometryProcessor {
725public:
Jim Van Verth10bf7022019-04-09 15:55:05 -0400726 DIEllipseGeometryProcessor(bool aa, bool wideColor, bool useScale, const SkMatrix& viewMatrix,
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400727 DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400728 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400729 , fViewMatrix(viewMatrix)
Jim Van Verth10bf7022019-04-09 15:55:05 -0400730 , fAA(aa)
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400731 , fUseScale(useScale)
732 , fStyle(style) {
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500733 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanc906d252018-12-04 11:17:46 -0500734 fInColor = MakeColorAttribute("inColor", wideColor);
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400735 if (useScale) {
736 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
737 kFloat3_GrSLType};
738 } else {
739 fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
740 kFloat2_GrSLType};
741 }
742 fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500743 this->setVertexAttributes(&fInPosition, 4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000744 }
745
Brian Salomond3b65972017-03-22 12:05:03 -0400746 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000747
mtklein36352bf2015-03-25 18:17:31 -0700748 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000749
Brian Salomon94efbf52016-11-29 13:43:05 -0500750 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700751 GLSLProcessor::GenKey(*this, caps, b);
752 }
halcanary9d524f22016-03-29 09:03:52 -0700753
Brian Salomon94efbf52016-11-29 13:43:05 -0500754 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700755 return new GLSLProcessor();
756 }
757
758private:
egdaniel57d3b032015-11-13 11:57:27 -0800759 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000760 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500761 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000762
joshualitt465283c2015-09-11 08:19:35 -0700763 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800764 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800765 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800766 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800767 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000768
joshualittabb52a12015-01-13 15:02:10 -0800769 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800770 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800771
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400772 GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
773 GrGLSLVarying offsets0(offsetType);
egdaniel0eafe792015-11-20 14:01:22 -0800774 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500775 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700776
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400777 GrGLSLVarying offsets1(kFloat2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800778 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500779 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800780
Chris Dalton60283612018-02-14 13:38:14 -0700781 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500782 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800783
joshualittabb52a12015-01-13 15:02:10 -0800784 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400785 this->writeOutputPosition(vertBuilder,
786 uniformHandler,
787 gpArgs,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500788 diegp.fInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400789 diegp.fViewMatrix,
790 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800791
792 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800793 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800794 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800795 uniformHandler,
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500796 diegp.fInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700797 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800798
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000799 // for outer curve
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400800 fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
801 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
802 fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
803 fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500804 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400805 "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
806 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500807 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400808 if (diegp.fUseScale) {
809 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
810 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000811
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400812 fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000813 // avoid calling inversesqrt on zero.
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400814 if (args.fShaderCaps->floatIs32Bits()) {
815 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
816 } else {
817 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
818 }
819 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
820 if (diegp.fUseScale) {
821 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
822 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800823 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000824 // can probably do this with one step
Jim Van Verth10bf7022019-04-09 15:55:05 -0400825 if (diegp.fAA) {
826 fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
827 fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
828 } else {
829 fragBuilder->codeAppend("float edgeAlpha = step(0, 0.5-test*invlen);");
830 fragBuilder->codeAppend("edgeAlpha *= step(0, 0.5+test*invlen);");
831 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000832 } else {
Jim Van Verth10bf7022019-04-09 15:55:05 -0400833 if (diegp.fAA) {
834 fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
835 } else {
836 fragBuilder->codeAppend("float edgeAlpha = step(0, -test*invlen);");
837 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000838 }
839
840 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800841 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800842 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
843 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400844 fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
845 fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500846 fragBuilder->codeAppendf(
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400847 "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
848 " %s.x*duvdy.x + %s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500849 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400850 if (diegp.fUseScale) {
851 fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
852 }
853 fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
854 if (!args.fShaderCaps->floatIs32Bits()) {
855 fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
856 }
857 fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
858 if (diegp.fUseScale) {
859 fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
860 }
Jim Van Verth10bf7022019-04-09 15:55:05 -0400861 if (diegp.fAA) {
862 fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
863 } else {
864 fragBuilder->codeAppend("edgeAlpha *= step(0, test*invlen);");
865 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000866 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000867
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400868 fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000869 }
870
robertphillips46d36f02015-01-18 08:14:14 -0800871 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500872 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700873 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800874 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
Jim Van Verth10bf7022019-04-09 15:55:05 -0400875 uint16_t key = diegp.fAA ? 0x1 : 0x0;
876 key |= static_cast<uint16_t>(diegp.fStyle) << 1;
877 key |= ComputePosKey(diegp.fViewMatrix) << 11;
joshualittb8c241a2015-05-19 08:23:30 -0700878 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000879 }
880
bsalomona624bf32016-09-20 09:12:47 -0700881 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
882 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800883 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700884
bsalomon31df31c2016-08-17 09:00:24 -0700885 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
886 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700887 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800888 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700889 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
890 }
bsalomona624bf32016-09-20 09:12:47 -0700891 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000892 }
893
894 private:
joshualitt5559ca22015-05-21 15:50:36 -0700895 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700896 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800897
egdaniele659a582015-11-13 09:55:43 -0800898 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000899 };
900
Brian Salomon92be2f72018-06-19 14:33:47 -0400901
Brian Osmanf04fb3c2018-11-12 15:34:00 -0500902 Attribute fInPosition;
903 Attribute fInColor;
904 Attribute fInEllipseOffsets0;
905 Attribute fInEllipseOffsets1;
Brian Salomon92be2f72018-06-19 14:33:47 -0400906
Brian Salomon289e3d82016-12-14 15:52:56 -0500907 SkMatrix fViewMatrix;
Jim Van Verth10bf7022019-04-09 15:55:05 -0400908 bool fAA;
Jim Van Verth20ae25c2019-03-29 08:50:41 -0400909 bool fUseScale;
Brian Salomon289e3d82016-12-14 15:52:56 -0500910 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000911
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400912 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000913
joshualitt249af152014-09-15 11:41:13 -0700914 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915};
916
bsalomoncdaa97b2016-03-08 08:30:14 -0800917GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000918
Hal Canary6f6961e2017-01-31 13:50:44 -0500919#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700920sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500921 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Jim Van Verth10bf7022019-04-09 15:55:05 -0400922 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
923 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000924}
Hal Canary6f6961e2017-01-31 13:50:44 -0500925#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000926
927///////////////////////////////////////////////////////////////////////////////
928
jvanverth6ca48822016-10-07 06:57:32 -0700929// We have two possible cases for geometry for a circle:
930
931// In the case of a normal fill, we draw geometry for the circle as an octagon.
932static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500933 // enter the octagon
934 // clang-format off
935 0, 1, 8, 1, 2, 8,
936 2, 3, 8, 3, 4, 8,
937 4, 5, 8, 5, 6, 8,
938 6, 7, 8, 7, 0, 8
939 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700940};
941
942// For stroked circles, we use two nested octagons.
943static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500944 // enter the octagon
945 // clang-format off
946 0, 1, 9, 0, 9, 8,
947 1, 2, 10, 1, 10, 9,
948 2, 3, 11, 2, 11, 10,
949 3, 4, 12, 3, 12, 11,
950 4, 5, 13, 4, 13, 12,
951 5, 6, 14, 5, 14, 13,
952 6, 7, 15, 6, 15, 14,
953 7, 0, 8, 7, 8, 15,
954 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700955};
956
Brian Osman9d958b52018-11-13 12:46:56 -0500957// Normalized geometry for octagons that circumscribe and lie on a circle:
958
959static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
960static constexpr SkPoint kOctagonOuter[] = {
961 SkPoint::Make(-kOctOffset, -1),
962 SkPoint::Make( kOctOffset, -1),
963 SkPoint::Make( 1, -kOctOffset),
964 SkPoint::Make( 1, kOctOffset),
965 SkPoint::Make( kOctOffset, 1),
966 SkPoint::Make(-kOctOffset, 1),
967 SkPoint::Make(-1, kOctOffset),
968 SkPoint::Make(-1, -kOctOffset),
969};
970
971// cosine and sine of pi/8
972static constexpr SkScalar kCosPi8 = 0.923579533f;
973static constexpr SkScalar kSinPi8 = 0.382683432f;
974static constexpr SkPoint kOctagonInner[] = {
975 SkPoint::Make(-kSinPi8, -kCosPi8),
976 SkPoint::Make( kSinPi8, -kCosPi8),
977 SkPoint::Make( kCosPi8, -kSinPi8),
978 SkPoint::Make( kCosPi8, kSinPi8),
979 SkPoint::Make( kSinPi8, kCosPi8),
980 SkPoint::Make(-kSinPi8, kCosPi8),
981 SkPoint::Make(-kCosPi8, kSinPi8),
982 SkPoint::Make(-kCosPi8, -kSinPi8),
983};
Brian Salomon289e3d82016-12-14 15:52:56 -0500984
jvanverth6ca48822016-10-07 06:57:32 -0700985static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
986static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
987static const int kVertsPerStrokeCircle = 16;
988static const int kVertsPerFillCircle = 9;
989
990static int circle_type_to_vert_count(bool stroked) {
991 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
992}
993
994static int circle_type_to_index_count(bool stroked) {
995 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
996}
997
998static const uint16_t* circle_type_to_indices(bool stroked) {
999 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
1000}
1001
1002///////////////////////////////////////////////////////////////////////////////
1003
Brian Salomon05441c42017-05-15 16:45:49 -04001004class CircleOp final : public GrMeshDrawOp {
1005private:
1006 using Helper = GrSimpleMeshDrawOpHelper;
1007
joshualitt76e7fb62015-02-11 08:52:27 -08001008public:
Brian Salomon25a88092016-12-01 09:36:50 -05001009 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001010
bsalomon4f3a0ca2016-08-22 13:14:26 -07001011 /** Optional extra params to render a partial arc rather than a full circle. */
1012 struct ArcParams {
1013 SkScalar fStartAngleRadians;
1014 SkScalar fSweepAngleRadians;
1015 bool fUseCenter;
1016 };
Brian Salomon05441c42017-05-15 16:45:49 -04001017
Robert Phillipsb97da532019-02-12 15:24:12 -05001018 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001019 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04001020 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04001021 const SkMatrix& viewMatrix,
1022 SkPoint center,
1023 SkScalar radius,
1024 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -04001025 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001026 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001027 if (style.hasPathEffect()) {
1028 return nullptr;
1029 }
Brian Salomon05441c42017-05-15 16:45:49 -04001030 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 SkStrokeRec::Style recStyle = stroke.getStyle();
1032 if (arcParams) {
1033 // Arc support depends on the style.
1034 switch (recStyle) {
1035 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -05001036 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001037 return nullptr;
1038 case SkStrokeRec::kFill_Style:
1039 // This supports all fills.
1040 break;
Brian Salomon45c92202018-04-10 10:53:58 -04001041 case SkStrokeRec::kStroke_Style:
1042 // Strokes that don't use the center point are supported with butt and round
1043 // caps.
1044 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
1045 return nullptr;
1046 }
1047 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001048 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -04001049 // Hairline only supports butt cap. Round caps could be emulated by slightly
1050 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001051 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
1052 return nullptr;
1053 }
1054 break;
1055 }
1056 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04001057 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), aaType, viewMatrix,
1058 center, radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -04001059 }
1060
Jim Van Verth10bf7022019-04-09 15:55:05 -04001061 CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
Brian Osmancf860852018-10-31 14:04:39 -04001062 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
1063 const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +00001064 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001065 const SkStrokeRec& stroke = style.strokeRec();
1066 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -07001067
Brian Salomon45c92202018-04-10 10:53:58 -04001068 fRoundCaps = false;
Jim Van Verth10bf7022019-04-09 15:55:05 -04001069 fAA = (GrAAType::kCoverage == aaType);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001070 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001071 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001072 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -08001073
Brian Salomon289e3d82016-12-14 15:52:56 -05001074 bool isStrokeOnly =
1075 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001076 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001077
jvanverth6ca48822016-10-07 06:57:32 -07001078 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001079 SkScalar outerRadius = radius;
1080 SkScalar halfWidth = 0;
1081 if (hasStroke) {
1082 if (SkScalarNearlyZero(strokeWidth)) {
1083 halfWidth = SK_ScalarHalf;
1084 } else {
1085 halfWidth = SkScalarHalf(strokeWidth);
1086 }
1087
1088 outerRadius += halfWidth;
1089 if (isStrokeOnly) {
1090 innerRadius = radius - halfWidth;
1091 }
1092 }
1093
1094 // The radii are outset for two reasons. First, it allows the shader to simply perform
1095 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1096 // Second, the outer radius is used to compute the verts of the bounding box that is
1097 // rendered and the outset ensures the box will cover all partially covered by the circle.
Jim Van Verth10bf7022019-04-09 15:55:05 -04001098 if (fAA) {
1099 outerRadius += SK_ScalarHalf;
1100 innerRadius -= SK_ScalarHalf;
1101 }
jvanverth6ca48822016-10-07 06:57:32 -07001102 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -04001103 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001104
bsalomon4f3a0ca2016-08-22 13:14:26 -07001105 // This makes every point fully inside the intersection plane.
1106 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
1107 // This makes every point fully outside the union plane.
1108 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -04001109 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1111 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001112 if (arcParams) {
1113 // The shader operates in a space where the circle is translated to be centered at the
1114 // origin. Here we compute points on the unit circle at the starting and ending angles.
1115 SkPoint startPoint, stopPoint;
Brian Osman4428f2c2019-04-02 10:59:28 -04001116 startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
1117 startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
Brian Osman4428f2c2019-04-02 10:59:28 -04001119 stopPoint.fY = SkScalarSin(endAngle);
1120 stopPoint.fX = SkScalarCos(endAngle);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001121
1122 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1123 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1124 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1125 startPoint.normalize();
1126 stopPoint.normalize();
1127
1128 // If the matrix included scale (on one axis) we need to swap our start and end points
1129 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001130 using std::swap;
1131 swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001132 }
1133
Brian Salomon45c92202018-04-10 10:53:58 -04001134 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1135 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1136 SkPoint roundCaps[2];
1137 if (fRoundCaps) {
1138 // Compute the cap center points in the normalized space.
1139 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1140 roundCaps[0] = startPoint * midRadius;
1141 roundCaps[1] = stopPoint * midRadius;
1142 } else {
1143 roundCaps[0] = kUnusedRoundCaps[0];
1144 roundCaps[1] = kUnusedRoundCaps[1];
1145 }
1146
bsalomon4f3a0ca2016-08-22 13:14:26 -07001147 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001148 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1149 // center of the butts.
1150 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001151 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001152 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001153 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001154 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1155 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1156 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001157 if (useCenter) {
1158 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1159 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
Brian Osman9a24fee2018-08-03 09:48:42 -04001160 // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
1161 if (arcParams->fSweepAngleRadians < 0) {
1162 std::swap(norm0, norm1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001163 }
Brian Osman9a24fee2018-08-03 09:48:42 -04001164 norm0.negate();
Brian Salomon05441c42017-05-15 16:45:49 -04001165 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001166 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001167 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001168 color,
1169 innerRadius,
1170 outerRadius,
1171 {norm0.fX, norm0.fY, 0.5f},
1172 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1173 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001174 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001175 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001176 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001177 fClipPlaneIsect = false;
1178 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001179 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001180 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001181 color,
1182 innerRadius,
1183 outerRadius,
1184 {norm0.fX, norm0.fY, 0.5f},
1185 {norm1.fX, norm1.fY, 0.5f},
1186 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001187 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001188 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001189 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001190 fClipPlaneIsect = true;
1191 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001192 }
1193 } else {
1194 // We clip to a secant of the original circle.
1195 startPoint.scale(radius);
1196 stopPoint.scale(radius);
1197 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1198 norm.normalize();
1199 if (arcParams->fSweepAngleRadians > 0) {
1200 norm.negate();
1201 }
1202 SkScalar d = -norm.dot(startPoint) + 0.5f;
1203
Brian Salomon05441c42017-05-15 16:45:49 -04001204 fCircles.emplace_back(
1205 Circle{color,
1206 innerRadius,
1207 outerRadius,
1208 {norm.fX, norm.fY, d},
1209 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1210 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001211 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001212 devBounds,
1213 stroked});
1214 fClipPlane = true;
1215 fClipPlaneIsect = false;
1216 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001217 }
1218 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001219 fCircles.emplace_back(
1220 Circle{color,
1221 innerRadius,
1222 outerRadius,
1223 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1224 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1225 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001226 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001227 devBounds,
1228 stroked});
1229 fClipPlane = false;
1230 fClipPlaneIsect = false;
1231 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001232 }
bsalomon88cf17d2016-07-08 06:40:56 -07001233 // Use the original radius and stroke radius for the bounds so that it does not include the
1234 // AA bloat.
1235 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001236 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001237 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
Jim Van Verth10bf7022019-04-09 15:55:05 -04001238 fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001239 fVertCount = circle_type_to_vert_count(stroked);
1240 fIndexCount = circle_type_to_index_count(stroked);
1241 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001242 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001243
Brian Salomon289e3d82016-12-14 15:52:56 -05001244 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001245
Brian Salomon7d94bb52018-10-12 14:37:19 -04001246 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001247 fHelper.visitProxies(func);
1248 }
1249
Brian Osman9a390ac2018-11-12 09:47:48 -05001250#ifdef SK_DEBUG
robertphillipse004bfc2015-11-16 09:06:59 -08001251 SkString dumpInfo() const override {
1252 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001253 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001254 string.appendf(
1255 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1256 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001257 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001258 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1259 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1260 fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001261 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001262 string += fHelper.dumpInfo();
1263 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001264 return string;
1265 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001266#endif
robertphillipse004bfc2015-11-16 09:06:59 -08001267
Brian Osman5ced0bf2019-03-15 10:15:29 -04001268 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1269 GrFSAAType fsaaType, GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001270 SkPMColor4f* color = &fCircles.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04001271 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001272 GrProcessorAnalysisCoverage::kSingleChannel, color,
1273 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001274 }
1275
1276 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1277
bsalomone46f9fe2015-08-18 06:05:14 -07001278private:
Brian Salomon91326c32017-08-09 16:02:19 -04001279 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001280 SkMatrix localMatrix;
1281 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001282 return;
1283 }
1284
1285 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001286 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Jim Van Verth10bf7022019-04-09 15:55:05 -04001287 fAA, !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps,
1288 fWideColor, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001289
Brian Salomon12d22642019-01-29 14:38:50 -05001290 sk_sp<const GrBuffer> vertexBuffer;
jvanverth6ca48822016-10-07 06:57:32 -07001291 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001292 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1293 &vertexBuffer, &firstVertex)};
1294 if (!vertices.fPtr) {
jvanverth6ca48822016-10-07 06:57:32 -07001295 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001296 return;
1297 }
1298
Brian Salomon12d22642019-01-29 14:38:50 -05001299 sk_sp<const GrBuffer> indexBuffer = nullptr;
jvanverth6ca48822016-10-07 06:57:32 -07001300 int firstIndex = 0;
1301 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1302 if (!indices) {
1303 SkDebugf("Could not allocate indices\n");
1304 return;
1305 }
1306
1307 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001308 for (const auto& circle : fCircles) {
1309 SkScalar innerRadius = circle.fInnerRadius;
1310 SkScalar outerRadius = circle.fOuterRadius;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001311 GrVertexColor color(circle.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001312 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001313
joshualitt76e7fb62015-02-11 08:52:27 -08001314 // The inner radius in the vertex data must be specified in normalized space.
1315 innerRadius = innerRadius / outerRadius;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001316 SkPoint radii = { outerRadius, innerRadius };
jvanverth6ca48822016-10-07 06:57:32 -07001317
1318 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001319 SkScalar halfWidth = 0.5f * bounds.width();
Herb Derby60c05f92016-12-13 15:18:55 -05001320
Brian Osman9a24fee2018-08-03 09:48:42 -04001321 SkVector geoClipPlane = { 0, 0 };
1322 SkScalar offsetClipDist = SK_Scalar1;
1323 if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
1324 (circle.fClipPlane[0] * circle.fIsectPlane[0] +
1325 circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
1326 // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
1327 // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
1328 // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
1329 // the AA can extend just past the center of the circle.
1330 geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
1331 circle.fIsectPlane[0] - circle.fClipPlane[0]);
1332 SkAssertResult(geoClipPlane.normalize());
1333 offsetClipDist = 0.5f / halfWidth;
1334 }
1335
Brian Osman7d8f82b2018-11-08 10:24:09 -05001336 for (int i = 0; i < 8; ++i) {
1337 // This clips the normalized offset to the half-plane we computed above. Then we
1338 // compute the vertex position from this.
Brian Osman9d958b52018-11-13 12:46:56 -05001339 SkScalar dist = SkTMin(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
1340 SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001341 vertices.write(center + offset * halfWidth,
1342 color,
1343 offset,
1344 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001345 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001346 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001347 }
1348 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001349 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001350 }
1351 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001352 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001353 }
1354 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001355 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001356 }
Brian Salomon45c92202018-04-10 10:53:58 -04001357 }
jvanverth6ca48822016-10-07 06:57:32 -07001358
Brian Salomon05441c42017-05-15 16:45:49 -04001359 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001360 // compute the inner ring
jvanverth6ca48822016-10-07 06:57:32 -07001361
Brian Osman7d8f82b2018-11-08 10:24:09 -05001362 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001363 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1364 color,
1365 kOctagonInner[i] * innerRadius,
1366 radii);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001367 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001368 vertices.write(circle.fClipPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001369 }
1370 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001371 vertices.write(circle.fIsectPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001372 }
1373 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001374 vertices.write(circle.fUnionPlane);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001375 }
1376 if (fRoundCaps) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001377 vertices.write(circle.fRoundCapCenters);
Brian Osman7d8f82b2018-11-08 10:24:09 -05001378 }
Brian Salomon45c92202018-04-10 10:53:58 -04001379 }
jvanverth6ca48822016-10-07 06:57:32 -07001380 } else {
1381 // filled
Brian Osmanf9aabff2018-11-13 16:11:38 -05001382 vertices.write(center, color, SkPoint::Make(0, 0), radii);
jvanverth6ca48822016-10-07 06:57:32 -07001383 if (fClipPlane) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001384 vertices.write(circle.fClipPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001385 }
jvanverth6ca48822016-10-07 06:57:32 -07001386 if (fClipPlaneIsect) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001387 vertices.write(circle.fIsectPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001388 }
1389 if (fClipPlaneUnion) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001390 vertices.write(circle.fUnionPlane);
jvanverth6ca48822016-10-07 06:57:32 -07001391 }
Brian Salomon5ceda552018-12-14 16:03:38 -05001392 if (fRoundCaps) {
1393 vertices.write(circle.fRoundCapCenters);
1394 }
jvanverth6ca48822016-10-07 06:57:32 -07001395 }
1396
Brian Salomon05441c42017-05-15 16:45:49 -04001397 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1398 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001399 for (int i = 0; i < primIndexCount; ++i) {
1400 *indices++ = primIndices[i] + currStartVertex;
1401 }
1402
Brian Salomon05441c42017-05-15 16:45:49 -04001403 currStartVertex += circle_type_to_vert_count(circle.fStroked);
joshualitt76e7fb62015-02-11 08:52:27 -08001404 }
jvanverth6ca48822016-10-07 06:57:32 -07001405
Brian Salomon7eae3e02018-08-07 14:02:38 +00001406 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05001407 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00001408 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05001409 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001410 target->recordDraw(std::move(gp), mesh);
1411 }
1412
1413 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
1414 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001415 }
1416
Brian Salomon7eae3e02018-08-07 14:02:38 +00001417 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001418 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001419
1420 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001421 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001422 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05001423 }
1424
Brian Salomon05441c42017-05-15 16:45:49 -04001425 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001426 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001427 }
1428
Brian Salomon05441c42017-05-15 16:45:49 -04001429 if (fHelper.usesLocalCoords() &&
1430 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001431 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001432 }
1433
Jim Van Verth10bf7022019-04-09 15:55:05 -04001434 if (fAA != that->fAA) {
1435 return CombineResult::kCannotCombine;
1436 }
1437
Brian Salomon289e3d82016-12-14 15:52:56 -05001438 // Because we've set up the ops that don't use the planes with noop values
1439 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001440 fClipPlane |= that->fClipPlane;
1441 fClipPlaneIsect |= that->fClipPlaneIsect;
1442 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001443 fRoundCaps |= that->fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001444 fWideColor |= that->fWideColor;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001445
Brian Salomon05441c42017-05-15 16:45:49 -04001446 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
jvanverth6ca48822016-10-07 06:57:32 -07001447 fVertCount += that->fVertCount;
1448 fIndexCount += that->fIndexCount;
1449 fAllFill = fAllFill && that->fAllFill;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001450 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001451 }
1452
Brian Salomon05441c42017-05-15 16:45:49 -04001453 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001454 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 SkScalar fInnerRadius;
1456 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001457 SkScalar fClipPlane[3];
1458 SkScalar fIsectPlane[3];
1459 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001460 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 SkRect fDevBounds;
1462 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001463 };
1464
Brian Salomon289e3d82016-12-14 15:52:56 -05001465 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001466 Helper fHelper;
1467 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001468 int fVertCount;
1469 int fIndexCount;
Jim Van Verth10bf7022019-04-09 15:55:05 -04001470 bool fAA;
Brian Salomon289e3d82016-12-14 15:52:56 -05001471 bool fAllFill;
1472 bool fClipPlane;
1473 bool fClipPlaneIsect;
1474 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001475 bool fRoundCaps;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001476 bool fWideColor;
reed1b55a962015-09-17 20:16:13 -07001477
Brian Salomon05441c42017-05-15 16:45:49 -04001478 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001479};
1480
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001481class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1482private:
1483 using Helper = GrSimpleMeshDrawOpHelper;
1484
1485public:
1486 DEFINE_OP_CLASS_ID
1487
Robert Phillipsb97da532019-02-12 15:24:12 -05001488 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001489 GrPaint&& paint,
1490 const SkMatrix& viewMatrix,
1491 SkPoint center,
1492 SkScalar radius,
1493 SkScalar strokeWidth,
1494 SkScalar startAngle,
1495 SkScalar onAngle,
1496 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001497 SkScalar phaseAngle) {
1498 SkASSERT(circle_stays_circle(viewMatrix));
1499 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001500 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1501 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001502 onAngle, offAngle, phaseAngle);
1503 }
1504
Brian Osmancf860852018-10-31 14:04:39 -04001505 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001506 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1507 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1508 SkScalar offAngle, SkScalar phaseAngle)
1509 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1510 SkASSERT(circle_stays_circle(viewMatrix));
1511 viewMatrix.mapPoints(&center, 1);
1512 radius = viewMatrix.mapRadius(radius);
1513 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1514
1515 // Determine the angle where the circle starts in device space and whether its orientation
1516 // has been reversed.
1517 SkVector start;
1518 bool reflection;
1519 if (!startAngle) {
1520 start = {1, 0};
1521 } else {
Brian Osman4428f2c2019-04-02 10:59:28 -04001522 start.fY = SkScalarSin(startAngle);
1523 start.fX = SkScalarCos(startAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001524 }
1525 viewMatrix.mapVectors(&start, 1);
1526 startAngle = SkScalarATan2(start.fY, start.fX);
1527 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1528 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1529
1530 auto totalAngle = onAngle + offAngle;
1531 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1532
1533 SkScalar halfWidth = 0;
1534 if (SkScalarNearlyZero(strokeWidth)) {
1535 halfWidth = SK_ScalarHalf;
1536 } else {
1537 halfWidth = SkScalarHalf(strokeWidth);
1538 }
1539
1540 SkScalar outerRadius = radius + halfWidth;
1541 SkScalar innerRadius = radius - halfWidth;
1542
1543 // The radii are outset for two reasons. First, it allows the shader to simply perform
1544 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1545 // Second, the outer radius is used to compute the verts of the bounding box that is
1546 // rendered and the outset ensures the box will cover all partially covered by the circle.
1547 outerRadius += SK_ScalarHalf;
1548 innerRadius -= SK_ScalarHalf;
1549 fViewMatrixIfUsingLocalCoords = viewMatrix;
1550
1551 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1552 center.fX + outerRadius, center.fY + outerRadius);
1553
1554 // We store whether there is a reflection as a negative total angle.
1555 if (reflection) {
1556 totalAngle = -totalAngle;
1557 }
1558 fCircles.push_back(Circle{
1559 color,
1560 outerRadius,
1561 innerRadius,
1562 onAngle,
1563 totalAngle,
1564 startAngle,
1565 phaseAngle,
1566 devBounds
1567 });
1568 // Use the original radius and stroke radius for the bounds so that it does not include the
1569 // AA bloat.
1570 radius += halfWidth;
1571 this->setBounds(
1572 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1573 HasAABloat::kYes, IsZeroArea::kNo);
1574 fVertCount = circle_type_to_vert_count(true);
1575 fIndexCount = circle_type_to_index_count(true);
1576 }
1577
1578 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1579
Brian Salomon7d94bb52018-10-12 14:37:19 -04001580 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
1581 fHelper.visitProxies(func);
1582 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001583
Brian Osman9a390ac2018-11-12 09:47:48 -05001584#ifdef SK_DEBUG
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001585 SkString dumpInfo() const override {
1586 SkString string;
1587 for (int i = 0; i < fCircles.count(); ++i) {
1588 string.appendf(
1589 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1590 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1591 "Phase: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001592 fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001593 fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
1594 fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
1595 fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
1596 fCircles[i].fPhaseAngle);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001597 }
1598 string += fHelper.dumpInfo();
1599 string += INHERITED::dumpInfo();
1600 return string;
1601 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001602#endif
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001603
Brian Osman5ced0bf2019-03-15 10:15:29 -04001604 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1605 GrFSAAType fsaaType, GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04001606 SkPMColor4f* color = &fCircles.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04001607 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001608 GrProcessorAnalysisCoverage::kSingleChannel, color,
1609 &fWideColor);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001610 }
1611
1612 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1613
1614private:
1615 void onPrepareDraws(Target* target) override {
1616 SkMatrix localMatrix;
1617 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1618 return;
1619 }
1620
1621 // Setup geometry processor
Brian Osmane3caf2d2018-11-21 13:48:36 -05001622 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(fWideColor,
1623 localMatrix));
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001624
Brian Salomon12d22642019-01-29 14:38:50 -05001625 sk_sp<const GrBuffer> vertexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001626 int firstVertex;
Brian Osmanf9aabff2018-11-13 16:11:38 -05001627 GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
1628 &vertexBuffer, &firstVertex)};
1629 if (!vertices.fPtr) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001630 SkDebugf("Could not allocate vertices\n");
1631 return;
1632 }
1633
Brian Salomon12d22642019-01-29 14:38:50 -05001634 sk_sp<const GrBuffer> indexBuffer;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001635 int firstIndex = 0;
1636 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1637 if (!indices) {
1638 SkDebugf("Could not allocate indices\n");
1639 return;
1640 }
1641
1642 int currStartVertex = 0;
1643 for (const auto& circle : fCircles) {
1644 // The inner radius in the vertex data must be specified in normalized space so that
1645 // length() can be called with smaller values to avoid precision issues with half
1646 // floats.
1647 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1648 const SkRect& bounds = circle.fDevBounds;
1649 bool reflect = false;
Brian Osman9d958b52018-11-13 12:46:56 -05001650 struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
1651 circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
1652 };
1653 if (dashParams.totalAngle < 0) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001654 reflect = true;
Brian Osman9d958b52018-11-13 12:46:56 -05001655 dashParams.totalAngle = -dashParams.totalAngle;
1656 dashParams.startAngle = -dashParams.startAngle;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001657 }
1658
Brian Osmane3caf2d2018-11-21 13:48:36 -05001659 GrVertexColor color(circle.fColor, fWideColor);
Brian Osman1be2b7c2018-10-29 16:07:15 -04001660
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001661 // The bounding geometry for the circle is composed of an outer bounding octagon and
1662 // an inner bounded octagon.
1663
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001664 // Compute the vertices of the outer octagon.
1665 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1666 SkScalar halfWidth = 0.5f * bounds.width();
Brian Osman9d958b52018-11-13 12:46:56 -05001667
1668 auto reflectY = [=](const SkPoint& p) {
1669 return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001670 };
Brian Osman9d958b52018-11-13 12:46:56 -05001671
1672 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001673 vertices.write(center + kOctagonOuter[i] * halfWidth,
1674 color,
1675 reflectY(kOctagonOuter[i]),
1676 circle.fOuterRadius,
1677 normInnerRadius,
1678 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001679 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001680
1681 // Compute the vertices of the inner octagon.
Brian Osman9d958b52018-11-13 12:46:56 -05001682 for (int i = 0; i < 8; ++i) {
Brian Osmanf9aabff2018-11-13 16:11:38 -05001683 vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
1684 color,
1685 reflectY(kOctagonInner[i]) * normInnerRadius,
1686 circle.fOuterRadius,
1687 normInnerRadius,
1688 dashParams);
Brian Osman9d958b52018-11-13 12:46:56 -05001689 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001690
1691 const uint16_t* primIndices = circle_type_to_indices(true);
1692 const int primIndexCount = circle_type_to_index_count(true);
1693 for (int i = 0; i < primIndexCount; ++i) {
1694 *indices++ = primIndices[i] + currStartVertex;
1695 }
1696
1697 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001698 }
1699
Brian Salomon7eae3e02018-08-07 14:02:38 +00001700 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05001701 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00001702 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05001703 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001704 target->recordDraw(std::move(gp), mesh);
1705 }
1706
1707 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
1708 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001709 }
1710
Brian Salomon7eae3e02018-08-07 14:02:38 +00001711 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001712 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1713
1714 // can only represent 65535 unique vertices with 16-bit indices
1715 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001716 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001717 }
1718
1719 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001720 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001721 }
1722
1723 if (fHelper.usesLocalCoords() &&
1724 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001725 return CombineResult::kCannotCombine;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001726 }
1727
1728 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001729 fVertCount += that->fVertCount;
1730 fIndexCount += that->fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001731 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001732 return CombineResult::kMerged;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001733 }
1734
1735 struct Circle {
Brian Osmancf860852018-10-31 14:04:39 -04001736 SkPMColor4f fColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001737 SkScalar fOuterRadius;
1738 SkScalar fInnerRadius;
1739 SkScalar fOnAngle;
1740 SkScalar fTotalAngle;
1741 SkScalar fStartAngle;
1742 SkScalar fPhaseAngle;
1743 SkRect fDevBounds;
1744 };
1745
1746 SkMatrix fViewMatrixIfUsingLocalCoords;
1747 Helper fHelper;
1748 SkSTArray<1, Circle, true> fCircles;
1749 int fVertCount;
1750 int fIndexCount;
Brian Osmane3caf2d2018-11-21 13:48:36 -05001751 bool fWideColor;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001752
1753 typedef GrMeshDrawOp INHERITED;
1754};
1755
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001756///////////////////////////////////////////////////////////////////////////////
1757
Brian Salomon05441c42017-05-15 16:45:49 -04001758class EllipseOp : public GrMeshDrawOp {
1759private:
1760 using Helper = GrSimpleMeshDrawOpHelper;
1761
1762 struct DeviceSpaceParams {
1763 SkPoint fCenter;
1764 SkScalar fXRadius;
1765 SkScalar fYRadius;
1766 SkScalar fInnerXRadius;
1767 SkScalar fInnerYRadius;
1768 };
1769
joshualitt76e7fb62015-02-11 08:52:27 -08001770public:
Brian Salomon25a88092016-12-01 09:36:50 -05001771 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001772
Robert Phillipsb97da532019-02-12 15:24:12 -05001773 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04001774 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04001775 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04001776 const SkMatrix& viewMatrix,
1777 const SkRect& ellipse,
1778 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001779 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001780 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001781 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1782 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001783 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1784 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001785 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1786 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1787 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1788 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001789
bsalomon4b4a7cc2016-07-08 04:42:54 -07001790 // do (potentially) anisotropic mapping of stroke
1791 SkVector scaledStroke;
1792 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001793 scaledStroke.fX = SkScalarAbs(
1794 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1795 scaledStroke.fY = SkScalarAbs(
1796 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001797
1798 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001799 bool isStrokeOnly =
1800 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001801 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1802
Brian Salomon05441c42017-05-15 16:45:49 -04001803 params.fInnerXRadius = 0;
1804 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001805 if (hasStroke) {
1806 if (SkScalarNearlyZero(scaledStroke.length())) {
1807 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1808 } else {
1809 scaledStroke.scale(SK_ScalarHalf);
1810 }
1811
1812 // we only handle thick strokes for near-circular ellipses
1813 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001814 (0.5f * params.fXRadius > params.fYRadius ||
1815 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001816 return nullptr;
1817 }
1818
1819 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001820 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1821 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1822 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1823 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001824 return nullptr;
1825 }
1826
1827 // this is legit only if scale & translation (which should be the case at the moment)
1828 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001829 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1830 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001831 }
1832
Brian Salomon05441c42017-05-15 16:45:49 -04001833 params.fXRadius += scaledStroke.fX;
1834 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001835 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001836
1837 // For large ovals with low precision floats, we fall back to the path renderer.
1838 // To compute the AA at the edge we divide by the gradient, which is clamped to a
1839 // minimum value to avoid divides by zero. With large ovals and low precision this
1840 // leads to blurring at the edge of the oval.
1841 const SkScalar kMaxOvalRadius = 16384;
1842 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
1843 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
1844 return nullptr;
1845 }
1846
Jim Van Verth10bf7022019-04-09 15:55:05 -04001847 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), aaType, viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04001848 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001849 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850
Brian Osmancf860852018-10-31 14:04:39 -04001851 EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
Jim Van Verth10bf7022019-04-09 15:55:05 -04001852 GrAAType aaType, const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
Brian Osman936fe7d2018-10-30 15:30:35 -04001853 const SkStrokeRec& stroke)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001854 : INHERITED(ClassID())
1855 , fHelper(helperArgs, GrAAType::kCoverage)
Jim Van Verth10bf7022019-04-09 15:55:05 -04001856 , fAA(GrAAType::kCoverage == aaType)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001857 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04001858 SkStrokeRec::Style style = stroke.getStyle();
1859 bool isStrokeOnly =
1860 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001861
Brian Salomon05441c42017-05-15 16:45:49 -04001862 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1863 params.fInnerXRadius, params.fInnerYRadius,
1864 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1865 params.fCenter.fY - params.fYRadius,
1866 params.fCenter.fX + params.fXRadius,
1867 params.fCenter.fY + params.fYRadius)});
1868
Jim Van Verth10bf7022019-04-09 15:55:05 -04001869 this->setBounds(fEllipses.back().fDevBounds, fAA ? HasAABloat::kYes : HasAABloat::kNo,
1870 IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001871
bsalomon4b4a7cc2016-07-08 04:42:54 -07001872 // Outset bounds to include half-pixel width antialiasing.
Jim Van Verth10bf7022019-04-09 15:55:05 -04001873 if (fAA) {
1874 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1875 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001876
Brian Salomon05441c42017-05-15 16:45:49 -04001877 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1878 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001879 }
joshualitt76e7fb62015-02-11 08:52:27 -08001880
Brian Salomon289e3d82016-12-14 15:52:56 -05001881 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001882
Brian Salomon7d94bb52018-10-12 14:37:19 -04001883 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001884 fHelper.visitProxies(func);
1885 }
1886
Brian Osman9a390ac2018-11-12 09:47:48 -05001887#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05001888 SkString dumpInfo() const override {
1889 SkString string;
1890 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001891 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001892 string.appendf(
1893 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1894 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04001895 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04001896 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
1897 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001898 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001899 string += fHelper.dumpInfo();
1900 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001901 return string;
1902 }
Brian Osman9a390ac2018-11-12 09:47:48 -05001903#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05001904
Brian Osman5ced0bf2019-03-15 10:15:29 -04001905 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
1906 GrFSAAType fsaaType, GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001907 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
1908 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04001909 SkPMColor4f* color = &fEllipses.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04001910 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04001911 GrProcessorAnalysisCoverage::kSingleChannel, color,
1912 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001913 }
1914
1915 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1916
bsalomone46f9fe2015-08-18 06:05:14 -07001917private:
Brian Salomon91326c32017-08-09 16:02:19 -04001918 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001919 SkMatrix localMatrix;
1920 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001921 return;
1922 }
1923
1924 // Setup geometry processor
Jim Van Verth10bf7022019-04-09 15:55:05 -04001925 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fAA, fStroked, fWideColor,
1926 fUseScale, localMatrix));
Brian Osman9d958b52018-11-13 12:46:56 -05001927 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05001928 GrVertexWriter verts{helper.vertices()};
1929 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08001930 return;
1931 }
1932
Brian Salomon05441c42017-05-15 16:45:49 -04001933 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05001934 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04001935 SkScalar xRadius = ellipse.fXRadius;
1936 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001937
1938 // Compute the reciprocals of the radii here to save time in the shader
Brian Osman9d958b52018-11-13 12:46:56 -05001939 struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
1940 SkScalarInvert(xRadius),
1941 SkScalarInvert(yRadius),
1942 SkScalarInvert(ellipse.fInnerXRadius),
1943 SkScalarInvert(ellipse.fInnerYRadius)
1944 };
Jim Van Verth10bf7022019-04-09 15:55:05 -04001945 SkScalar xMaxOffset = xRadius;
1946 SkScalar yMaxOffset = yRadius;
1947 if (fAA) {
1948 xMaxOffset += SK_ScalarHalf;
1949 yMaxOffset += SK_ScalarHalf;
1950 }
vjiaoblack977996d2016-06-30 12:20:54 -07001951
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001952 if (!fStroked) {
1953 // For filled ellipses we map a unit circle in the vertex attributes rather than
1954 // computing an ellipse and modifying that distance, so we normalize to 1
1955 xMaxOffset /= xRadius;
1956 yMaxOffset /= yRadius;
1957 }
1958
joshualitt76e7fb62015-02-11 08:52:27 -08001959 // The inner radius in the vertex data must be specified in normalized space.
Brian Osman2b6e3902018-11-21 15:29:43 -05001960 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
1961 color,
1962 origin_centered_tri_strip(xMaxOffset, yMaxOffset),
Jim Van Verth20ae25c2019-03-29 08:50:41 -04001963 GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05001964 invRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08001965 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -07001966 helper.recordDraw(target, std::move(gp));
1967 }
1968
1969 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
1970 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001971 }
1972
Brian Salomon7eae3e02018-08-07 14:02:38 +00001973 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001974 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001975
Brian Salomon05441c42017-05-15 16:45:49 -04001976 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001977 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07001978 }
1979
bsalomoncdaa97b2016-03-08 08:30:14 -08001980 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001981 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001982 }
1983
Jim Van Verth10bf7022019-04-09 15:55:05 -04001984 if (fAA != that->fAA) {
1985 return CombineResult::kCannotCombine;
1986 }
1987
Brian Salomon05441c42017-05-15 16:45:49 -04001988 if (fHelper.usesLocalCoords() &&
1989 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00001990 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08001991 }
1992
Brian Salomon05441c42017-05-15 16:45:49 -04001993 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05001994 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00001995 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08001996 }
1997
Brian Salomon05441c42017-05-15 16:45:49 -04001998 struct Ellipse {
Brian Osmancf860852018-10-31 14:04:39 -04001999 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002000 SkScalar fXRadius;
2001 SkScalar fYRadius;
2002 SkScalar fInnerXRadius;
2003 SkScalar fInnerYRadius;
2004 SkRect fDevBounds;
2005 };
joshualitt76e7fb62015-02-11 08:52:27 -08002006
Brian Salomon289e3d82016-12-14 15:52:56 -05002007 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002008 Helper fHelper;
Jim Van Verth10bf7022019-04-09 15:55:05 -04002009 bool fAA;
Brian Salomon05441c42017-05-15 16:45:49 -04002010 bool fStroked;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002011 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002012 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002013 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002014
Brian Salomon05441c42017-05-15 16:45:49 -04002015 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002016};
2017
joshualitt76e7fb62015-02-11 08:52:27 -08002018/////////////////////////////////////////////////////////////////////////////////////////////////
2019
Brian Salomon05441c42017-05-15 16:45:49 -04002020class DIEllipseOp : public GrMeshDrawOp {
2021private:
2022 using Helper = GrSimpleMeshDrawOpHelper;
2023
2024 struct DeviceSpaceParams {
2025 SkPoint fCenter;
2026 SkScalar fXRadius;
2027 SkScalar fYRadius;
2028 SkScalar fInnerXRadius;
2029 SkScalar fInnerYRadius;
2030 DIEllipseStyle fStyle;
2031 };
2032
joshualitt76e7fb62015-02-11 08:52:27 -08002033public:
Brian Salomon25a88092016-12-01 09:36:50 -05002034 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002035
Robert Phillipsb97da532019-02-12 15:24:12 -05002036 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002037 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04002038 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04002039 const SkMatrix& viewMatrix,
2040 const SkRect& ellipse,
2041 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002042 DeviceSpaceParams params;
2043 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2044 params.fXRadius = SkScalarHalf(ellipse.width());
2045 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002046
bsalomon4b4a7cc2016-07-08 04:42:54 -07002047 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002048 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2049 ? DIEllipseStyle::kStroke
2050 : (SkStrokeRec::kHairline_Style == style)
2051 ? DIEllipseStyle::kHairline
2052 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002053
Brian Salomon05441c42017-05-15 16:45:49 -04002054 params.fInnerXRadius = 0;
2055 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002056 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2057 SkScalar strokeWidth = stroke.getWidth();
2058
2059 if (SkScalarNearlyZero(strokeWidth)) {
2060 strokeWidth = SK_ScalarHalf;
2061 } else {
2062 strokeWidth *= SK_ScalarHalf;
2063 }
2064
2065 // we only handle thick strokes for near-circular ellipses
2066 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002067 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2068 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002069 return nullptr;
2070 }
2071
2072 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002073 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2074 (strokeWidth * strokeWidth) * params.fXRadius) {
2075 return nullptr;
2076 }
2077 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2078 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002079 return nullptr;
2080 }
2081
2082 // set inner radius (if needed)
2083 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002084 params.fInnerXRadius = params.fXRadius - strokeWidth;
2085 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002086 }
2087
Brian Salomon05441c42017-05-15 16:45:49 -04002088 params.fXRadius += strokeWidth;
2089 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002090 }
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002091
2092 // For large ovals with low precision floats, we fall back to the path renderer.
2093 // To compute the AA at the edge we divide by the gradient, which is clamped to a
2094 // minimum value to avoid divides by zero. With large ovals and low precision this
2095 // leads to blurring at the edge of the oval.
2096 const SkScalar kMaxOvalRadius = 16384;
2097 if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
2098 (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
2099 return nullptr;
2100 }
2101
Brian Salomon05441c42017-05-15 16:45:49 -04002102 if (DIEllipseStyle::kStroke == params.fStyle &&
2103 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2104 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002105 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04002106 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), aaType, params,
2107 viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002108 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002109
Jim Van Verth10bf7022019-04-09 15:55:05 -04002110 DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
Brian Osman936fe7d2018-10-30 15:30:35 -04002111 const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002112 : INHERITED(ClassID())
2113 , fHelper(helperArgs, GrAAType::kCoverage)
Jim Van Verth10bf7022019-04-09 15:55:05 -04002114 , fAA(GrAAType::kCoverage == aaType)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002115 , fUseScale(false) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04002116 SkScalar geoDx = 0;
2117 SkScalar geoDy = 0;
2118 if (fAA) {
2119 // This expands the outer rect so that after CTM we end up with a half-pixel border
2120 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2121 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2122 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2123 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
2124 geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2125 geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
2126 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002127
Brian Salomon05441c42017-05-15 16:45:49 -04002128 fEllipses.emplace_back(
2129 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2130 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2131 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2132 params.fCenter.fY - params.fYRadius - geoDy,
2133 params.fCenter.fX + params.fXRadius + geoDx,
2134 params.fCenter.fY + params.fYRadius + geoDy)});
Jim Van Verth10bf7022019-04-09 15:55:05 -04002135 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix,
2136 fAA ? HasAABloat::kYes : HasAABloat::kNo,
Brian Salomon05441c42017-05-15 16:45:49 -04002137 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002138 }
2139
Brian Salomon289e3d82016-12-14 15:52:56 -05002140 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002141
Brian Salomon7d94bb52018-10-12 14:37:19 -04002142 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002143 fHelper.visitProxies(func);
2144 }
2145
Brian Osman9a390ac2018-11-12 09:47:48 -05002146#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002147 SkString dumpInfo() const override {
2148 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002149 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002150 string.appendf(
2151 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2152 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2153 "GeoDY: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002154 geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002155 geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
2156 geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002157 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002158 string += fHelper.dumpInfo();
2159 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002160 return string;
2161 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002162#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002163
Brian Osman5ced0bf2019-03-15 10:15:29 -04002164 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2165 GrFSAAType fsaaType, GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002166 fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
2167 !caps.shaderCaps()->hasLowFragmentPrecision();
Brian Osmancf860852018-10-31 14:04:39 -04002168 SkPMColor4f* color = &fEllipses.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04002169 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002170 GrProcessorAnalysisCoverage::kSingleChannel, color,
2171 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002172 }
2173
2174 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2175
bsalomone46f9fe2015-08-18 06:05:14 -07002176private:
Brian Salomon91326c32017-08-09 16:02:19 -04002177 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002178 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002179 sk_sp<GrGeometryProcessor> gp(
Jim Van Verth10bf7022019-04-09 15:55:05 -04002180 new DIEllipseGeometryProcessor(fAA, fWideColor, fUseScale, this->viewMatrix(),
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002181 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002182
Brian Osman9d958b52018-11-13 12:46:56 -05002183 QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
Brian Osmanf9aabff2018-11-13 16:11:38 -05002184 GrVertexWriter verts{helper.vertices()};
2185 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002186 return;
2187 }
2188
Brian Salomon05441c42017-05-15 16:45:49 -04002189 for (const auto& ellipse : fEllipses) {
Brian Osmane3caf2d2018-11-21 13:48:36 -05002190 GrVertexColor color(ellipse.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002191 SkScalar xRadius = ellipse.fXRadius;
2192 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002193
joshualitt76e7fb62015-02-11 08:52:27 -08002194 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002195 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2196 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002197
Brian Osman9d958b52018-11-13 12:46:56 -05002198 // By default, constructed so that inner offset is (0, 0) for all points
2199 SkScalar innerRatioX = -offsetDx;
2200 SkScalar innerRatioY = -offsetDy;
joshualitt76e7fb62015-02-11 08:52:27 -08002201
Brian Osman9d958b52018-11-13 12:46:56 -05002202 // ... unless we're stroked
Greg Daniel75a13022018-04-04 08:59:20 -04002203 if (DIEllipseStyle::kStroke == this->style()) {
Brian Osman9d958b52018-11-13 12:46:56 -05002204 innerRatioX = xRadius / ellipse.fInnerXRadius;
2205 innerRatioY = yRadius / ellipse.fInnerYRadius;
Greg Daniel75a13022018-04-04 08:59:20 -04002206 }
joshualitt76e7fb62015-02-11 08:52:27 -08002207
Brian Osman2b6e3902018-11-21 15:29:43 -05002208 verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
2209 color,
2210 origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002211 GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)),
Brian Osman2b6e3902018-11-21 15:29:43 -05002212 origin_centered_tri_strip(innerRatioX + offsetDx,
2213 innerRatioY + offsetDy));
joshualitt76e7fb62015-02-11 08:52:27 -08002214 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002215 helper.recordDraw(target, std::move(gp));
2216 }
2217
2218 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2219 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08002220 }
halcanary9d524f22016-03-29 09:03:52 -07002221
Brian Salomon7eae3e02018-08-07 14:02:38 +00002222 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002223 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002224 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002225 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002226 }
2227
Jim Van Verth10bf7022019-04-09 15:55:05 -04002228 if (this->fAA != that->fAA) {
2229 return CombineResult::kCannotCombine;
2230 }
2231
bsalomoncdaa97b2016-03-08 08:30:14 -08002232 if (this->style() != that->style()) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002233 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002234 }
2235
joshualittd96a67b2015-05-05 14:09:05 -07002236 // TODO rewrite to allow positioning on CPU
2237 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002238 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002239 }
2240
Brian Salomon05441c42017-05-15 16:45:49 -04002241 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
Brian Osmane3caf2d2018-11-21 13:48:36 -05002242 fWideColor |= that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002243 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002244 }
2245
Brian Salomon05441c42017-05-15 16:45:49 -04002246 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2247 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002248
Brian Salomon05441c42017-05-15 16:45:49 -04002249 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002250 SkMatrix fViewMatrix;
Brian Osmancf860852018-10-31 14:04:39 -04002251 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002252 SkScalar fXRadius;
2253 SkScalar fYRadius;
2254 SkScalar fInnerXRadius;
2255 SkScalar fInnerYRadius;
2256 SkScalar fGeoDx;
2257 SkScalar fGeoDy;
2258 DIEllipseStyle fStyle;
2259 SkRect fBounds;
2260 };
2261
Brian Salomon05441c42017-05-15 16:45:49 -04002262 Helper fHelper;
Jim Van Verth10bf7022019-04-09 15:55:05 -04002263 bool fAA;
Brian Osmane3caf2d2018-11-21 13:48:36 -05002264 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002265 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002266 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002267
Brian Salomon05441c42017-05-15 16:45:49 -04002268 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002269};
2270
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002271///////////////////////////////////////////////////////////////////////////////
2272
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002273// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002274//
2275// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2276// ____________
2277// |_|________|_|
2278// | | | |
2279// | | | |
2280// | | | |
2281// |_|________|_|
2282// |_|________|_|
2283//
2284// For strokes, we don't draw the center quad.
2285//
2286// For circular roundrects, in the case where the stroke width is greater than twice
2287// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002288// in the center. The shared vertices are duplicated so we can set a different outer radius
2289// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002290// ____________
2291// |_|________|_|
2292// | |\ ____ /| |
2293// | | | | | |
2294// | | |____| | |
2295// |_|/______\|_|
2296// |_|________|_|
2297//
2298// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002299//
2300// For filled rrects that need to provide a distance vector we resuse the overstroke
2301// geometry but make the inner rect degenerate (either a point or a horizontal or
2302// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002303
jvanverth84839f62016-08-29 10:16:40 -07002304static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002305 // clang-format off
2306 // overstroke quads
2307 // we place this at the beginning so that we can skip these indices when rendering normally
2308 16, 17, 19, 16, 19, 18,
2309 19, 17, 23, 19, 23, 21,
2310 21, 23, 22, 21, 22, 20,
2311 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002312
Brian Salomon289e3d82016-12-14 15:52:56 -05002313 // corners
2314 0, 1, 5, 0, 5, 4,
2315 2, 3, 7, 2, 7, 6,
2316 8, 9, 13, 8, 13, 12,
2317 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002318
Brian Salomon289e3d82016-12-14 15:52:56 -05002319 // edges
2320 1, 2, 6, 1, 6, 5,
2321 4, 5, 9, 4, 9, 8,
2322 6, 7, 11, 6, 11, 10,
2323 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002324
Brian Salomon289e3d82016-12-14 15:52:56 -05002325 // center
2326 // we place this at the end so that we can ignore these indices when not rendering as filled
2327 5, 6, 10, 5, 10, 9,
2328 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002329};
Brian Salomon289e3d82016-12-14 15:52:56 -05002330
jvanverth84839f62016-08-29 10:16:40 -07002331// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002332static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002333
jvanverth84839f62016-08-29 10:16:40 -07002334// overstroke count is arraysize minus the center indices
2335static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2336// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002337static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002338// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002339static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2340static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002341static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002342
jvanverthc3d0e422016-08-25 08:12:35 -07002343enum RRectType {
2344 kFill_RRectType,
2345 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002346 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002347};
2348
jvanverth84839f62016-08-29 10:16:40 -07002349static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002350 switch (type) {
2351 case kFill_RRectType:
2352 case kStroke_RRectType:
2353 return kVertsPerStandardRRect;
2354 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002355 return kVertsPerOverstrokeRRect;
2356 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002357 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002358 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002359}
2360
2361static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002362 switch (type) {
2363 case kFill_RRectType:
2364 return kIndicesPerFillRRect;
2365 case kStroke_RRectType:
2366 return kIndicesPerStrokeRRect;
2367 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002368 return kIndicesPerOverstrokeRRect;
2369 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002370 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002371 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002372}
2373
2374static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002375 switch (type) {
2376 case kFill_RRectType:
2377 case kStroke_RRectType:
2378 return gStandardRRectIndices;
2379 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002380 return gOverstrokeRRectIndices;
2381 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002382 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002383 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002384}
2385
joshualitt76e7fb62015-02-11 08:52:27 -08002386///////////////////////////////////////////////////////////////////////////////////////////////////
2387
Robert Phillips79839d42016-10-06 15:03:34 -04002388// For distance computations in the interior of filled rrects we:
2389//
2390// add a interior degenerate (point or line) rect
2391// each vertex of that rect gets -outerRad as its radius
2392// this makes the computation of the distance to the outer edge be negative
2393// negative values are caught and then handled differently in the GP's onEmitCode
2394// each vertex is also given the normalized x & y distance from the interior rect's edge
2395// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2396
Brian Salomon05441c42017-05-15 16:45:49 -04002397class CircularRRectOp : public GrMeshDrawOp {
2398private:
2399 using Helper = GrSimpleMeshDrawOpHelper;
2400
joshualitt76e7fb62015-02-11 08:52:27 -08002401public:
Brian Salomon25a88092016-12-01 09:36:50 -05002402 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002403
bsalomon4b4a7cc2016-07-08 04:42:54 -07002404 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2405 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002406 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002407 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04002408 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04002409 const SkMatrix& viewMatrix,
2410 const SkRect& devRect,
2411 float devRadius,
2412 float devStrokeWidth,
2413 bool strokeOnly) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04002414 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), aaType, viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04002415 devRect, devRadius,
2416 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002417 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04002418 CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color, GrAAType aaType,
Brian Osman936fe7d2018-10-30 15:30:35 -04002419 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
2420 float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002421 : INHERITED(ClassID())
2422 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Jim Van Verth10bf7022019-04-09 15:55:05 -04002423 , fHelper(helperArgs, GrAAType::kCoverage)
2424 , fAA(GrAAType::kCoverage == aaType) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002425 SkRect bounds = devRect;
2426 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2427 SkScalar innerRadius = 0.0f;
2428 SkScalar outerRadius = devRadius;
2429 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002430 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002431 if (devStrokeWidth > 0) {
2432 if (SkScalarNearlyZero(devStrokeWidth)) {
2433 halfWidth = SK_ScalarHalf;
2434 } else {
2435 halfWidth = SkScalarHalf(devStrokeWidth);
2436 }
joshualitt76e7fb62015-02-11 08:52:27 -08002437
bsalomon4b4a7cc2016-07-08 04:42:54 -07002438 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002439 // Outset stroke by 1/4 pixel
2440 devStrokeWidth += 0.25f;
2441 // If stroke is greater than width or height, this is still a fill
2442 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002443 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002444 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002445 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002446 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002447 }
2448 outerRadius += halfWidth;
2449 bounds.outset(halfWidth, halfWidth);
2450 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002451
Jim Van Verth10bf7022019-04-09 15:55:05 -04002452 this->setBounds(bounds, fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002453
Jim Van Verth10bf7022019-04-09 15:55:05 -04002454 if (fAA) {
2455 // The radii are outset for two reasons. First, it allows the shader to simply perform
2456 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2457 // Second, the outer radius is used to compute the verts of the bounding box that is
2458 // rendered and the outset ensures the box will cover all partially covered by the rrect
2459 // corners.
2460 outerRadius += SK_ScalarHalf;
2461 innerRadius -= SK_ScalarHalf;
bsalomon88cf17d2016-07-08 06:40:56 -07002462
Jim Van Verth10bf7022019-04-09 15:55:05 -04002463 // Expand the rect for aa to generate correct vertices.
2464 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2465 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002466
Brian Salomon05441c42017-05-15 16:45:49 -04002467 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002468 fVertCount = rrect_type_to_vert_count(type);
2469 fIndexCount = rrect_type_to_index_count(type);
2470 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002471 }
2472
Brian Salomon289e3d82016-12-14 15:52:56 -05002473 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002474
Brian Salomon7d94bb52018-10-12 14:37:19 -04002475 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002476 fHelper.visitProxies(func);
2477 }
2478
Brian Osman9a390ac2018-11-12 09:47:48 -05002479#ifdef SK_DEBUG
jvanverthc3d0e422016-08-25 08:12:35 -07002480 SkString dumpInfo() const override {
2481 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002482 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002483 string.appendf(
2484 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2485 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002486 fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002487 fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
2488 fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
2489 fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002490 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002491 string += fHelper.dumpInfo();
2492 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002493 return string;
2494 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002495#endif
jvanverthc3d0e422016-08-25 08:12:35 -07002496
Brian Osman5ced0bf2019-03-15 10:15:29 -04002497 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2498 GrFSAAType fsaaType, GrClampType clampType) override {
Brian Osmancf860852018-10-31 14:04:39 -04002499 SkPMColor4f* color = &fRRects.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04002500 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002501 GrProcessorAnalysisCoverage::kSingleChannel, color,
2502 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002503 }
2504
2505 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2506
Brian Salomon92aee3d2016-12-21 09:20:25 -05002507private:
Brian Osmana1d4eb92018-12-06 16:33:10 -05002508 static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002509 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002510 SkScalar innerRadius, const GrVertexColor& color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002511 SkASSERT(smInset < bigInset);
2512
2513 // TL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002514 verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
2515 color,
2516 xOffset, 0.0f,
2517 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002518
2519 // TR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002520 verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
2521 color,
2522 xOffset, 0.0f,
2523 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002524
Brian Osmana1d4eb92018-12-06 16:33:10 -05002525 verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
2526 color,
2527 0.0f, 0.0f,
2528 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002529
Brian Osmana1d4eb92018-12-06 16:33:10 -05002530 verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
2531 color,
2532 0.0f, 0.0f,
2533 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002534
Brian Osmana1d4eb92018-12-06 16:33:10 -05002535 verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
2536 color,
2537 0.0f, 0.0f,
2538 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002539
Brian Osmana1d4eb92018-12-06 16:33:10 -05002540 verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
2541 color,
2542 0.0f, 0.0f,
2543 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002544
2545 // BL
Brian Osmana1d4eb92018-12-06 16:33:10 -05002546 verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
2547 color,
2548 xOffset, 0.0f,
2549 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002550
2551 // BR
Brian Osmana1d4eb92018-12-06 16:33:10 -05002552 verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
2553 color,
2554 xOffset, 0.0f,
2555 outerRadius, innerRadius);
Robert Phillips79839d42016-10-06 15:03:34 -04002556 }
2557
Brian Salomon91326c32017-08-09 16:02:19 -04002558 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002559 // Invert the view matrix as a local matrix (if any other processors require coords).
2560 SkMatrix localMatrix;
2561 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002562 return;
2563 }
2564
2565 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002566 sk_sp<GrGeometryProcessor> gp(
Jim Van Verth10bf7022019-04-09 15:55:05 -04002567 new CircleGeometryProcessor(fAA, !fAllFill, false, false, false, false,
2568 fWideColor, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002569
Brian Salomon12d22642019-01-29 14:38:50 -05002570 sk_sp<const GrBuffer> vertexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002571 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002572
Brian Osmana1d4eb92018-12-06 16:33:10 -05002573 GrVertexWriter verts{target->makeVertexSpace(gp->vertexStride(), fVertCount,
2574 &vertexBuffer, &firstVertex)};
2575 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002576 SkDebugf("Could not allocate vertices\n");
2577 return;
2578 }
2579
Brian Salomon12d22642019-01-29 14:38:50 -05002580 sk_sp<const GrBuffer> indexBuffer;
jvanverth84839f62016-08-29 10:16:40 -07002581 int firstIndex = 0;
2582 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2583 if (!indices) {
2584 SkDebugf("Could not allocate indices\n");
2585 return;
2586 }
2587
2588 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002589 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002590 GrVertexColor color(rrect.fColor, fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002591 SkScalar outerRadius = rrect.fOuterRadius;
2592 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002593
Brian Salomon289e3d82016-12-14 15:52:56 -05002594 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2595 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002596
Brian Salomon289e3d82016-12-14 15:52:56 -05002597 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002598 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002599 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002600 SkScalar innerRadius = rrect.fType != kFill_RRectType
2601 ? rrect.fInnerRadius / rrect.fOuterRadius
2602 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002603 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002604 verts.write(bounds.fLeft, yCoords[i],
2605 color,
2606 -1.0f, yOuterRadii[i],
2607 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002608
Brian Osmana1d4eb92018-12-06 16:33:10 -05002609 verts.write(bounds.fLeft + outerRadius, yCoords[i],
2610 color,
2611 0.0f, yOuterRadii[i],
2612 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002613
Brian Osmana1d4eb92018-12-06 16:33:10 -05002614 verts.write(bounds.fRight - outerRadius, yCoords[i],
2615 color,
2616 0.0f, yOuterRadii[i],
2617 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002618
Brian Osmana1d4eb92018-12-06 16:33:10 -05002619 verts.write(bounds.fRight, yCoords[i],
2620 color,
2621 1.0f, yOuterRadii[i],
2622 outerRadius, innerRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002623 }
jvanverthc3d0e422016-08-25 08:12:35 -07002624 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002625 // Effectively this is an additional stroked rrect, with its
2626 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2627 // This will give us correct AA in the center and the correct
2628 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002629 //
jvanvertha4f1af82016-08-29 07:17:47 -07002630 // Also, the outer offset is a constant vector pointing to the right, which
2631 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002632 if (kOverstroke_RRectType == rrect.fType) {
2633 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002634
Brian Salomon05441c42017-05-15 16:45:49 -04002635 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002636 // this is the normalized distance from the outer rectangle of this
2637 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002638 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002639
Brian Osmana1d4eb92018-12-06 16:33:10 -05002640 FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002641 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04002642 }
jvanverth6a397612016-08-26 08:15:33 -07002643
Brian Salomon05441c42017-05-15 16:45:49 -04002644 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2645 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002646 for (int i = 0; i < primIndexCount; ++i) {
2647 *indices++ = primIndices[i] + currStartVertex;
2648 }
2649
Brian Salomon05441c42017-05-15 16:45:49 -04002650 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002651 }
2652
Brian Salomon7eae3e02018-08-07 14:02:38 +00002653 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
Brian Salomon12d22642019-01-29 14:38:50 -05002654 mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
Brian Salomon7eae3e02018-08-07 14:02:38 +00002655 GrPrimitiveRestart::kNo);
Brian Salomon12d22642019-01-29 14:38:50 -05002656 mesh->setVertexData(std::move(vertexBuffer), firstVertex);
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002657 target->recordDraw(std::move(gp), mesh);
2658 }
2659
2660 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2661 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08002662 }
2663
Brian Salomon7eae3e02018-08-07 14:02:38 +00002664 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002665 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002666
2667 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002668 if (fVertCount + that->fVertCount > 65536) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002669 return CombineResult::kCannotCombine;
Jim Van Verth8cefe402017-02-09 11:36:37 -05002670 }
2671
Brian Salomon05441c42017-05-15 16:45:49 -04002672 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002673 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002674 }
2675
Brian Salomon05441c42017-05-15 16:45:49 -04002676 if (fHelper.usesLocalCoords() &&
2677 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002678 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002679 }
2680
Jim Van Verth10bf7022019-04-09 15:55:05 -04002681 if (fAA != that->fAA) {
2682 return CombineResult::kCannotCombine;
2683 }
2684
Brian Salomon05441c42017-05-15 16:45:49 -04002685 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
jvanverth84839f62016-08-29 10:16:40 -07002686 fVertCount += that->fVertCount;
2687 fIndexCount += that->fIndexCount;
2688 fAllFill = fAllFill && that->fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002689 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002690 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002691 }
2692
Brian Salomon05441c42017-05-15 16:45:49 -04002693 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002694 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002695 SkScalar fInnerRadius;
2696 SkScalar fOuterRadius;
2697 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002698 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002699 };
2700
Brian Salomon289e3d82016-12-14 15:52:56 -05002701 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002702 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002703 int fVertCount;
2704 int fIndexCount;
Jim Van Verth10bf7022019-04-09 15:55:05 -04002705 bool fAA;
Brian Salomon289e3d82016-12-14 15:52:56 -05002706 bool fAllFill;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002707 bool fWideColor;
Brian Salomon05441c42017-05-15 16:45:49 -04002708 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002709
Brian Salomon05441c42017-05-15 16:45:49 -04002710 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002711};
2712
jvanverth84839f62016-08-29 10:16:40 -07002713static const int kNumRRectsInIndexBuffer = 256;
2714
2715GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2716GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002717static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2718 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002719 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2720 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2721 switch (type) {
2722 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002723 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002724 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2725 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002726 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002727 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002728 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2729 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002730 default:
2731 SkASSERT(false);
2732 return nullptr;
Brian Salomon23356442018-11-30 15:33:19 -05002733 }
jvanverth84839f62016-08-29 10:16:40 -07002734}
2735
Brian Salomon05441c42017-05-15 16:45:49 -04002736class EllipticalRRectOp : public GrMeshDrawOp {
2737private:
2738 using Helper = GrSimpleMeshDrawOpHelper;
2739
joshualitt76e7fb62015-02-11 08:52:27 -08002740public:
Brian Salomon25a88092016-12-01 09:36:50 -05002741 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002742
bsalomon4b4a7cc2016-07-08 04:42:54 -07002743 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2744 // whether the rrect is only stroked or stroked and filled.
Robert Phillipsb97da532019-02-12 15:24:12 -05002745 static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04002746 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04002747 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04002748 const SkMatrix& viewMatrix,
2749 const SkRect& devRect,
2750 float devXRadius,
2751 float devYRadius,
2752 SkVector devStrokeWidths,
2753 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002754 SkASSERT(devXRadius > 0.5);
2755 SkASSERT(devYRadius > 0.5);
2756 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2757 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002758 if (devStrokeWidths.fX > 0) {
2759 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2760 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2761 } else {
2762 devStrokeWidths.scale(SK_ScalarHalf);
2763 }
joshualitt76e7fb62015-02-11 08:52:27 -08002764
bsalomon4b4a7cc2016-07-08 04:42:54 -07002765 // we only handle thick strokes for near-circular ellipses
2766 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002767 (SK_ScalarHalf * devXRadius > devYRadius ||
2768 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002769 return nullptr;
2770 }
2771
2772 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002773 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2774 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002775 return nullptr;
2776 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002777 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2778 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002779 return nullptr;
2780 }
Brian Salomon05441c42017-05-15 16:45:49 -04002781 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002782 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
Jim Van Verth10bf7022019-04-09 15:55:05 -04002783 aaType, viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002784 devXRadius, devYRadius, devStrokeWidths,
2785 strokeOnly);
2786 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002787
Jim Van Verth10bf7022019-04-09 15:55:05 -04002788 EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color, GrAAType aaType,
Brian Osman936fe7d2018-10-30 15:30:35 -04002789 const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
2790 float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002791 : INHERITED(ClassID())
2792 , fHelper(helperArgs, GrAAType::kCoverage)
Jim Van Verth10bf7022019-04-09 15:55:05 -04002793 , fAA(GrAAType::kCoverage == aaType)
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002794 , fUseScale(false) {
Brian Salomon05441c42017-05-15 16:45:49 -04002795 SkScalar innerXRadius = 0.0f;
2796 SkScalar innerYRadius = 0.0f;
2797 SkRect bounds = devRect;
2798 bool stroked = false;
2799 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002800 // this is legit only if scale & translation (which should be the case at the moment)
2801 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002802 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2803 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002804 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2805 }
2806
Brian Salomon05441c42017-05-15 16:45:49 -04002807 devXRadius += devStrokeHalfWidths.fX;
2808 devYRadius += devStrokeHalfWidths.fY;
2809 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002810 }
2811
Brian Salomon05441c42017-05-15 16:45:49 -04002812 fStroked = stroked;
2813 fViewMatrixIfUsingLocalCoords = viewMatrix;
Jim Van Verth10bf7022019-04-09 15:55:05 -04002814 this->setBounds(bounds, fAA ? HasAABloat::kYes : HasAABloat::kNo, IsZeroArea::kNo);
2815 if (fAA) {
2816 // Expand the rect for aa in order to generate the correct vertices.
2817 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2818 }
Brian Salomon05441c42017-05-15 16:45:49 -04002819 fRRects.emplace_back(
2820 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002821 }
2822
Brian Salomon289e3d82016-12-14 15:52:56 -05002823 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002824
Brian Salomon7d94bb52018-10-12 14:37:19 -04002825 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002826 fHelper.visitProxies(func);
2827 }
2828
Brian Osman9a390ac2018-11-12 09:47:48 -05002829#ifdef SK_DEBUG
Brian Salomon7c3e7182016-12-01 09:35:30 -05002830 SkString dumpInfo() const override {
2831 SkString string;
2832 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002833 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002834 string.appendf(
2835 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2836 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
Brian Osmancf860852018-10-31 14:04:39 -04002837 geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
Brian Osman1be2b7c2018-10-29 16:07:15 -04002838 geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
2839 geo.fInnerXRadius, geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002840 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002841 string += fHelper.dumpInfo();
2842 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002843 return string;
2844 }
Brian Osman9a390ac2018-11-12 09:47:48 -05002845#endif
Brian Salomon7c3e7182016-12-01 09:35:30 -05002846
Brian Osman5ced0bf2019-03-15 10:15:29 -04002847 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
2848 GrFSAAType fsaaType, GrClampType clampType) override {
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002849 fUseScale = !caps.shaderCaps()->floatIs32Bits();
Brian Osmancf860852018-10-31 14:04:39 -04002850 SkPMColor4f* color = &fRRects.front().fColor;
Brian Osman5ced0bf2019-03-15 10:15:29 -04002851 return fHelper.finalizeProcessors(caps, clip, fsaaType, clampType,
Brian Osman8fa7ab42019-03-18 10:22:42 -04002852 GrProcessorAnalysisCoverage::kSingleChannel, color,
2853 &fWideColor);
Brian Salomon05441c42017-05-15 16:45:49 -04002854 }
2855
2856 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2857
bsalomone46f9fe2015-08-18 06:05:14 -07002858private:
Brian Salomon91326c32017-08-09 16:02:19 -04002859 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002860 SkMatrix localMatrix;
2861 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002862 return;
2863 }
2864
2865 // Setup geometry processor
Jim Van Verth10bf7022019-04-09 15:55:05 -04002866 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fAA, fStroked, fWideColor, fUseScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002867 localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002868
bsalomonb5238a72015-05-05 07:49:49 -07002869 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002870 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002871 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2872 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002873
Brian Salomon12d22642019-01-29 14:38:50 -05002874 if (!indexBuffer) {
2875 SkDebugf("Could not allocate indices\n");
2876 return;
2877 }
Brian Osmana1d4eb92018-12-06 16:33:10 -05002878 PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
Brian Salomon12d22642019-01-29 14:38:50 -05002879 std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
Brian Salomon7eae3e02018-08-07 14:02:38 +00002880 fRRects.count());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002881 GrVertexWriter verts{helper.vertices()};
Brian Salomon12d22642019-01-29 14:38:50 -05002882 if (!verts.fPtr) {
joshualitt4b31de82015-03-05 14:33:41 -08002883 SkDebugf("Could not allocate vertices\n");
2884 return;
2885 }
2886
Brian Salomon05441c42017-05-15 16:45:49 -04002887 for (const auto& rrect : fRRects) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002888 GrVertexColor color(rrect.fColor, fWideColor);
joshualitt76e7fb62015-02-11 08:52:27 -08002889 // Compute the reciprocals of the radii here to save time in the shader
Brian Osmana1d4eb92018-12-06 16:33:10 -05002890 float reciprocalRadii[4] = {
2891 SkScalarInvert(rrect.fXRadius),
2892 SkScalarInvert(rrect.fYRadius),
2893 SkScalarInvert(rrect.fInnerXRadius),
2894 SkScalarInvert(rrect.fInnerYRadius)
2895 };
joshualitt76e7fb62015-02-11 08:52:27 -08002896
2897 // Extend the radii out half a pixel to antialias.
Jim Van Verth10bf7022019-04-09 15:55:05 -04002898 SkScalar xOuterRadius = rrect.fXRadius;
2899 SkScalar yOuterRadius = rrect.fYRadius;
2900 if (fAA) {
2901 xOuterRadius += SK_ScalarHalf;
2902 yOuterRadius += SK_ScalarHalf;
2903 }
joshualitt76e7fb62015-02-11 08:52:27 -08002904
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002905 SkScalar xMaxOffset = xOuterRadius;
2906 SkScalar yMaxOffset = yOuterRadius;
2907 if (!fStroked) {
2908 // For filled rrects we map a unit circle in the vertex attributes rather than
2909 // computing an ellipse and modifying that distance, so we normalize to 1.
2910 xMaxOffset /= rrect.fXRadius;
2911 yMaxOffset /= rrect.fYRadius;
2912 }
2913
Brian Salomon05441c42017-05-15 16:45:49 -04002914 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002915
Brian Salomon289e3d82016-12-14 15:52:56 -05002916 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2917 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002918 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002919 SK_ScalarNearlyZero, // we're using inversesqrt() in
2920 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002921 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08002922
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002923 auto maybeScale = GrVertexWriter::If(fUseScale, SkTMax(rrect.fXRadius, rrect.fYRadius));
joshualitt76e7fb62015-02-11 08:52:27 -08002924 for (int i = 0; i < 4; ++i) {
Brian Osmana1d4eb92018-12-06 16:33:10 -05002925 verts.write(bounds.fLeft, yCoords[i],
2926 color,
2927 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002928 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002929 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002930
Brian Osmana1d4eb92018-12-06 16:33:10 -05002931 verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
2932 color,
2933 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002934 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002935 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002936
Brian Osmana1d4eb92018-12-06 16:33:10 -05002937 verts.write(bounds.fRight - xOuterRadius, yCoords[i],
2938 color,
2939 SK_ScalarNearlyZero, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002940 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002941 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002942
Brian Osmana1d4eb92018-12-06 16:33:10 -05002943 verts.write(bounds.fRight, yCoords[i],
2944 color,
2945 xMaxOffset, yOuterOffsets[i],
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002946 maybeScale,
Brian Osmana1d4eb92018-12-06 16:33:10 -05002947 reciprocalRadii);
joshualitt76e7fb62015-02-11 08:52:27 -08002948 }
2949 }
Chris Dalton07cdcfc92019-02-26 11:13:22 -07002950 helper.recordDraw(target, std::move(gp));
2951 }
2952
2953 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
2954 fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08002955 }
2956
Brian Salomon7eae3e02018-08-07 14:02:38 +00002957 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002958 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002959
Brian Salomon05441c42017-05-15 16:45:49 -04002960 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002961 return CombineResult::kCannotCombine;
joshualitt8cab9a72015-07-16 09:13:50 -07002962 }
2963
bsalomoncdaa97b2016-03-08 08:30:14 -08002964 if (fStroked != that->fStroked) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002965 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002966 }
2967
Jim Van Verth10bf7022019-04-09 15:55:05 -04002968 if (fAA != that->fAA) {
2969 return CombineResult::kCannotCombine;
2970 }
2971
Brian Salomon05441c42017-05-15 16:45:49 -04002972 if (fHelper.usesLocalCoords() &&
2973 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
Brian Salomon7eae3e02018-08-07 14:02:38 +00002974 return CombineResult::kCannotCombine;
joshualitt76e7fb62015-02-11 08:52:27 -08002975 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002976
Brian Salomon05441c42017-05-15 16:45:49 -04002977 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
Brian Osmana1d4eb92018-12-06 16:33:10 -05002978 fWideColor = fWideColor || that->fWideColor;
Brian Salomon7eae3e02018-08-07 14:02:38 +00002979 return CombineResult::kMerged;
joshualitt76e7fb62015-02-11 08:52:27 -08002980 }
2981
Brian Salomon05441c42017-05-15 16:45:49 -04002982 struct RRect {
Brian Osmancf860852018-10-31 14:04:39 -04002983 SkPMColor4f fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002984 SkScalar fXRadius;
2985 SkScalar fYRadius;
2986 SkScalar fInnerXRadius;
2987 SkScalar fInnerYRadius;
2988 SkRect fDevBounds;
2989 };
2990
Brian Salomon289e3d82016-12-14 15:52:56 -05002991 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002992 Helper fHelper;
Jim Van Verth10bf7022019-04-09 15:55:05 -04002993 bool fAA;
Brian Salomon05441c42017-05-15 16:45:49 -04002994 bool fStroked;
Brian Osmana1d4eb92018-12-06 16:33:10 -05002995 bool fWideColor;
Jim Van Verth20ae25c2019-03-29 08:50:41 -04002996 bool fUseScale;
Brian Salomon05441c42017-05-15 16:45:49 -04002997 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002998
Brian Salomon05441c42017-05-15 16:45:49 -04002999 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003000};
3001
Robert Phillipsb97da532019-02-12 15:24:12 -05003002static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003003 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04003004 GrAAType aaType,
Brian Salomon05441c42017-05-15 16:45:49 -04003005 const SkMatrix& viewMatrix,
3006 const SkRRect& rrect,
3007 const SkStrokeRec& stroke) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003008 if (GrAAType::kMSAA == aaType) {
3009 return nullptr;
3010 }
3011
joshualitt3e708c52015-04-30 13:49:27 -07003012 SkASSERT(viewMatrix.rectStaysRect());
3013 SkASSERT(rrect.isSimple());
3014 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003015
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003016 // RRect ops only handle simple, but not too simple, rrects.
3017 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003018 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003019 SkRect bounds;
3020 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003021
Mike Reed242135a2018-02-22 13:41:39 -05003022 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003023 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3024 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3025 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3026 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003027
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003028 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003029
bsalomon4b4a7cc2016-07-08 04:42:54 -07003030 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3031 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003032 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003033
Brian Salomon289e3d82016-12-14 15:52:56 -05003034 bool isStrokeOnly =
3035 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003036 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3037
jvanverthc3d0e422016-08-25 08:12:35 -07003038 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003039 if (hasStroke) {
3040 if (SkStrokeRec::kHairline_Style == style) {
3041 scaledStroke.set(1, 1);
3042 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003043 scaledStroke.fX = SkScalarAbs(
3044 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3045 scaledStroke.fY = SkScalarAbs(
3046 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003047 }
3048
jvanverthc3d0e422016-08-25 08:12:35 -07003049 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
3050 // for non-circular rrects, if half of strokewidth is greater than radius,
3051 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05003052 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
3053 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003054 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003055 }
3056 }
3057
3058 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3059 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3060 // patch will have fractional coverage. This only matters when the interior is actually filled.
3061 // We could consider falling back to rect rendering here, since a tiny radius is
3062 // indistinguishable from a square corner.
3063 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003064 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003065 }
3066
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003067 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003068 if (isCircular) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003069 return CircularRRectOp::Make(context, std::move(paint), aaType, viewMatrix, bounds, xRadius,
Robert Phillips7c525e62018-06-12 10:11:12 -04003070 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003071 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003072 } else {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003073 return EllipticalRRectOp::Make(context, std::move(paint), aaType, viewMatrix, bounds,
Robert Phillips7c525e62018-06-12 10:11:12 -04003074 xRadius, yRadius, scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003075 }
joshualitt3e708c52015-04-30 13:49:27 -07003076}
3077
Robert Phillipsb97da532019-02-12 15:24:12 -05003078std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003079 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04003080 GrAAType aaType,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003081 const SkMatrix& viewMatrix,
3082 const SkRRect& rrect,
3083 const SkStrokeRec& stroke,
3084 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003085 if (rrect.isOval()) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003086 return MakeOvalOp(context, std::move(paint), aaType, viewMatrix, rrect.getBounds(),
Robert Phillips7c525e62018-06-12 10:11:12 -04003087 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003088 }
3089
3090 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003091 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003092 }
3093
Jim Van Verth10bf7022019-04-09 15:55:05 -04003094 return make_rrect_op(context, std::move(paint), aaType, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003095}
joshualitt3e708c52015-04-30 13:49:27 -07003096
bsalomon4b4a7cc2016-07-08 04:42:54 -07003097///////////////////////////////////////////////////////////////////////////////
3098
Robert Phillipsb97da532019-02-12 15:24:12 -05003099std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003100 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04003101 GrAAType aaType,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003102 const SkMatrix& viewMatrix,
3103 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003104 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003105 const GrShaderCaps* shaderCaps) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003106 if (GrAAType::kMSAA == aaType) {
3107 return nullptr;
3108 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07003109 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003110 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003111 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3112 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003113 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003114 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003115 if (style.hasNonDashPathEffect()) {
3116 return nullptr;
3117 } else if (style.isDashed()) {
3118 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3119 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3120 return nullptr;
3121 }
3122 auto onInterval = style.dashIntervals()[0];
3123 auto offInterval = style.dashIntervals()[1];
3124 if (offInterval == 0) {
3125 GrStyle strokeStyle(style.strokeRec(), nullptr);
Jim Van Verth10bf7022019-04-09 15:55:05 -04003126 return MakeOvalOp(context, std::move(paint), aaType, viewMatrix, oval,
Robert Phillips7c525e62018-06-12 10:11:12 -04003127 strokeStyle, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003128 } else if (onInterval == 0) {
3129 // There is nothing to draw but we have no way to indicate that here.
3130 return nullptr;
3131 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04003132 // TODO: handle non-AA butt caps
3133 if (GrAAType::kCoverage != aaType) {
3134 return nullptr;
3135 }
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003136 auto angularOnInterval = onInterval / r;
3137 auto angularOffInterval = offInterval / r;
3138 auto phaseAngle = style.dashPhase() / r;
3139 // Currently this function doesn't accept ovals with different start angles, though
3140 // it could.
3141 static const SkScalar kStartAngle = 0.f;
Robert Phillips7c525e62018-06-12 10:11:12 -04003142 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003143 style.strokeRec().getWidth(), kStartAngle,
3144 angularOnInterval, angularOffInterval, phaseAngle);
3145 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04003146 return CircleOp::Make(context, std::move(paint), aaType, viewMatrix, center, r, style);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003147 }
3148
3149 if (style.pathEffect()) {
3150 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003151 }
3152
Stan Ilieveb868aa2017-02-21 11:06:16 -05003153 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003154 if (viewMatrix.rectStaysRect()) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003155 return EllipseOp::Make(context, std::move(paint), aaType, viewMatrix, oval,
3156 style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003157 }
3158
Stan Ilieveb868aa2017-02-21 11:06:16 -05003159 // Otherwise, if we have shader derivative support, render as device-independent
3160 if (shaderCaps->shaderDerivativeSupport()) {
Jim Van Vertha925bb02018-09-25 10:49:52 -04003161 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
3162 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
3163 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
3164 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
3165 // Check for near-degenerate matrix
3166 if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003167 return DIEllipseOp::Make(context, std::move(paint), aaType, viewMatrix, oval,
Jim Van Vertha925bb02018-09-25 10:49:52 -04003168 style.strokeRec());
3169 }
Stan Ilieveb868aa2017-02-21 11:06:16 -05003170 }
3171
bsalomon4b4a7cc2016-07-08 04:42:54 -07003172 return nullptr;
3173}
3174
3175///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003176
Robert Phillipsb97da532019-02-12 15:24:12 -05003177std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
Robert Phillips7c525e62018-06-12 10:11:12 -04003178 GrPaint&& paint,
Jim Van Verth10bf7022019-04-09 15:55:05 -04003179 GrAAType aaType,
Robert Phillips7c525e62018-06-12 10:11:12 -04003180 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003181 const SkRect& oval, SkScalar startAngle,
3182 SkScalar sweepAngle, bool useCenter,
3183 const GrStyle& style,
3184 const GrShaderCaps* shaderCaps) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003185 if (GrAAType::kMSAA == aaType) {
3186 return nullptr;
3187 }
bsalomon21af9ca2016-08-25 12:29:23 -07003188 SkASSERT(!oval.isEmpty());
3189 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003190 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003191 if (SkScalarAbs(sweepAngle) >= 360.f) {
3192 return nullptr;
3193 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003194 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3195 return nullptr;
3196 }
3197 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003198 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3199 useCenter};
Jim Van Verth10bf7022019-04-09 15:55:05 -04003200 return CircleOp::Make(context, std::move(paint), aaType, viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003201 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003202}
3203
3204///////////////////////////////////////////////////////////////////////////////
3205
Hal Canary6f6961e2017-01-31 13:50:44 -05003206#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003207
Brian Salomon05441c42017-05-15 16:45:49 -04003208GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003209 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003210 SkScalar rotate = random->nextSScalar1() * 360.f;
3211 SkScalar translateX = random->nextSScalar1() * 1000.f;
3212 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003213 SkScalar scale;
3214 do {
3215 scale = random->nextSScalar1() * 100.f;
3216 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003217 SkMatrix viewMatrix;
3218 viewMatrix.setRotate(rotate);
3219 viewMatrix.postTranslate(translateX, translateY);
3220 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003221 SkRect circle = GrTest::TestSquare(random);
3222 SkPoint center = {circle.centerX(), circle.centerY()};
3223 SkScalar radius = circle.width() / 2.f;
Jim Van Verth10bf7022019-04-09 15:55:05 -04003224 // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
3225 GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003226 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003227 CircleOp::ArcParams arcParamsTmp;
3228 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003229 if (random->nextBool()) {
3230 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003231 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3232 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003233 arcParams = &arcParamsTmp;
3234 }
Jim Van Verth10bf7022019-04-09 15:55:05 -04003235 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), aaType, viewMatrix,
Robert Phillips7c525e62018-06-12 10:11:12 -04003236 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003237 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003238 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003239 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003240 }
Mike Klein16885072018-12-11 09:54:31 -05003241 assert_alive(paint);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003242 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003243}
3244
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003245GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3246 SkScalar rotate = random->nextSScalar1() * 360.f;
3247 SkScalar translateX = random->nextSScalar1() * 1000.f;
3248 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003249 SkScalar scale;
3250 do {
3251 scale = random->nextSScalar1() * 100.f;
3252 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003253 SkMatrix viewMatrix;
3254 viewMatrix.setRotate(rotate);
3255 viewMatrix.postTranslate(translateX, translateY);
3256 viewMatrix.postScale(scale, scale);
3257 SkRect circle = GrTest::TestSquare(random);
3258 SkPoint center = {circle.centerX(), circle.centerY()};
3259 SkScalar radius = circle.width() / 2.f;
3260 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3261 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3262 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3263 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3264 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003265 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3266 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003267 startAngle, onAngle, offAngle, phase);
3268}
3269
Brian Salomon05441c42017-05-15 16:45:49 -04003270GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003271 // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
3272 GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
joshualitt3e708c52015-04-30 13:49:27 -07003273 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003274 SkRect ellipse = GrTest::TestSquare(random);
Jim Van Verth10bf7022019-04-09 15:55:05 -04003275 return EllipseOp::Make(context, std::move(paint), aaType, viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003276 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003277}
3278
Brian Salomon05441c42017-05-15 16:45:49 -04003279GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003280 // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
3281 GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
joshualitt3e708c52015-04-30 13:49:27 -07003282 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003283 SkRect ellipse = GrTest::TestSquare(random);
Jim Van Verth10bf7022019-04-09 15:55:05 -04003284 return DIEllipseOp::Make(context, std::move(paint), aaType, viewMatrix, ellipse,
Robert Phillips7c525e62018-06-12 10:11:12 -04003285 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003286}
3287
Brian Salomon05441c42017-05-15 16:45:49 -04003288GR_DRAW_OP_TEST_DEFINE(RRectOp) {
Jim Van Verth10bf7022019-04-09 15:55:05 -04003289 // Only test kNone and kCoverage aaTypes (kMSAA returns nullptr)
3290 GrAAType aaType = random->nextBool() ? GrAAType::kNone : GrAAType::kCoverage;
joshualitt3e708c52015-04-30 13:49:27 -07003291 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003292 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Jim Van Verth10bf7022019-04-09 15:55:05 -04003293 return make_rrect_op(context, std::move(paint), aaType, viewMatrix, rrect,
Robert Phillips7c525e62018-06-12 10:11:12 -04003294 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003295}
3296
3297#endif