blob: 2ff01f4060e23c959728ef508a90472bec7dd194 [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"
Brian Salomon5ec9def2016-12-20 15:34:05 -05009#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080010#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050011#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080012#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070013#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050014#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070015#include "GrStyle.h"
Mike Reed242135a2018-02-22 13:41:39 -050016#include "SkRRectPriv.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080018#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080019#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070020#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080021#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080022#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050023#include "glsl/GrGLSLVarying.h"
Chris Daltonc17bf322017-10-24 10:59:03 -060024#include "glsl/GrGLSLVertexGeoBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050025#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040026#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080027
commit-bot@chromium.org81312832013-03-22 18:34:09 +000028namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080029
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050031 SkPoint fPos;
32 GrColor fColor;
33 SkPoint fOffset;
34 SkPoint fOuterRadii;
35 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000036};
37
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000038struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050039 SkPoint fPos;
40 GrColor fColor;
41 SkPoint fOuterOffset;
42 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000043};
44
Brian Salomon289e3d82016-12-14 15:52:56 -050045static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000046}
47
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000048///////////////////////////////////////////////////////////////////////////////
49
50/**
bsalomonce1c8862014-12-15 07:11:22 -080051 * The output of this effect is a modulation of the input color and coverage for a circle. It
52 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080053 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080054 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec4f : (p.xy, outerRad, innerRad)
57 * p is the position in the normalized space.
58 * outerRad is the outerRadius in device space.
59 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * Additional clip planes are supported for rendering circular arcs. The additional planes are
61 * either intersected or unioned together. Up to three planes are supported (an initial plane,
62 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050063 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070064 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040065 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
66 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000067 */
68
bsalomoncdaa97b2016-03-08 08:30:14 -080069class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070public:
Brian Salomonea26d6b2018-01-23 20:33:21 +000071 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Salomon45c92202018-04-10 10:53:58 -040072 bool roundCaps, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040073 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040074 , fLocalMatrix(localMatrix)
75 , fStroke(stroke) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040076 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
77 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
78 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070079 if (clipPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040080 fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070081 } else {
82 fInClipPlane = nullptr;
83 }
84 if (isectPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040085 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070086 } else {
87 fInIsectPlane = nullptr;
88 }
89 if (unionPlane) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -040090 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070091 } else {
92 fInUnionPlane = nullptr;
93 }
Brian Salomon45c92202018-04-10 10:53:58 -040094 if (roundCaps) {
95 SkASSERT(stroke);
96 SkASSERT(clipPlane);
97 fInRoundCapCenters =
98 &this->addVertexAttrib("inRoundCapCenters", kFloat4_GrVertexAttribType);
99 } else {
100 fInRoundCapCenters = nullptr;
101 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000102 }
103
Brian Salomond3b65972017-03-22 12:05:03 -0400104 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105
mtklein36352bf2015-03-25 18:17:31 -0700106 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
Brian Salomon94efbf52016-11-29 13:43:05 -0500108 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700109 GLSLProcessor::GenKey(*this, caps, b);
110 }
111
Brian Salomon94efbf52016-11-29 13:43:05 -0500112 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700113 return new GLSLProcessor();
114 }
115
116private:
egdaniel57d3b032015-11-13 11:57:27 -0800117 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000118 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800119 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000120
Brian Salomon289e3d82016-12-14 15:52:56 -0500121 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800122 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800123 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800124 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800125 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700126 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800127
joshualittabb52a12015-01-13 15:02:10 -0800128 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800129 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400130 fragBuilder->codeAppend("float4 circleEdge;");
Chris Daltonfdde34e2017-10-16 14:15:26 -0600131 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700132 if (cgp.fInClipPlane) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400133 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
135 }
136 if (cgp.fInIsectPlane) {
137 SkASSERT(cgp.fInClipPlane);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400138 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700139 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
140 }
141 if (cgp.fInUnionPlane) {
142 SkASSERT(cgp.fInClipPlane);
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400143 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700144 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
145 }
Brian Salomon45c92202018-04-10 10:53:58 -0400146 GrGLSLVarying capRadius(kFloat_GrSLType);
147 if (cgp.fInRoundCapCenters) {
148 fragBuilder->codeAppend("float4 roundCapCenters;");
149 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
150 varyingHandler->addVarying("capRadius", &capRadius,
151 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
152 // This is the cap radius in normalized space where the outer radius is 1 and
153 // circledEdge.w is the normalized inner radius.
154 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
155 cgp.fInCircleEdge->fName);
156 }
joshualittabb52a12015-01-13 15:02:10 -0800157
joshualittb8c241a2015-05-19 08:23:30 -0700158 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700159 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800160
joshualittabb52a12015-01-13 15:02:10 -0800161 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400162 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800163
164 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800165 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800166 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800167 uniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500168 cgp.fInPosition->asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700169 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700170 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800171
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400172 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400173 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
174 fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800175 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500176 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400177 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
178 fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800179 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000180 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000181
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500183 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400184 "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
Brian Salomon289e3d82016-12-14 15:52:56 -0500185 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500187 fragBuilder->codeAppend(
188 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
189 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 }
191 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500192 fragBuilder->codeAppend(
193 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
194 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700195 }
196 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon45c92202018-04-10 10:53:58 -0400197 if (cgp.fInRoundCapCenters) {
Brian Salomon33c44222018-04-19 08:44:21 -0400198 // We compute coverage of the round caps as circles at the butt caps produced
199 // by the clip planes. The inverse of the clip planes is applied so that there
200 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400201 fragBuilder->codeAppendf(
202 "half dcap1 = circleEdge.z * (%s - length(circleEdge.xy - "
203 " roundCapCenters.xy));"
204 "half dcap2 = circleEdge.z * (%s - length(circleEdge.xy - "
205 " roundCapCenters.zw));"
Brian Salomon33c44222018-04-19 08:44:21 -0400206 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400207 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
208 capRadius.fsIn(), capRadius.fsIn());
209 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700210 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000211 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000212 }
213
robertphillips46d36f02015-01-18 08:14:14 -0800214 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500215 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700216 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800217 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700218 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500219 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700220 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500221 key |= cgp.fInClipPlane ? 0x04 : 0x0;
222 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
223 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
Brian Salomon45c92202018-04-10 10:53:58 -0400224 key |= cgp.fInRoundCapCenters ? 0x20 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700225 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 }
227
bsalomona624bf32016-09-20 09:12:47 -0700228 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
229 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700230 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700231 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700232 }
233
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234 private:
egdaniele659a582015-11-13 09:55:43 -0800235 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000236 };
237
Brian Salomon289e3d82016-12-14 15:52:56 -0500238 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800239 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800240 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800241 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700242 const Attribute* fInClipPlane;
243 const Attribute* fInIsectPlane;
244 const Attribute* fInUnionPlane;
Brian Salomon45c92202018-04-10 10:53:58 -0400245 const Attribute* fInRoundCapCenters;
Brian Salomon289e3d82016-12-14 15:52:56 -0500246 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400247 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248
joshualitt249af152014-09-15 11:41:13 -0700249 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250};
251
bsalomoncdaa97b2016-03-08 08:30:14 -0800252GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253
Hal Canary6f6961e2017-01-31 13:50:44 -0500254#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700255sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400256 bool stroke = d->fRandom->nextBool();
257 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
258 bool clipPlane = d->fRandom->nextBool();
259 bool isectPlane = d->fRandom->nextBool();
260 bool unionPlane = d->fRandom->nextBool();
261 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
262 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(stroke, roundCaps, clipPlane,
263 isectPlane, unionPlane, matrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264}
Hal Canary6f6961e2017-01-31 13:50:44 -0500265#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400267class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
268public:
269 ButtCapDashedCircleGeometryProcessor(const SkMatrix& localMatrix)
270 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
271 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
272 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
273 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType);
274 fInDashParams = &this->addVertexAttrib("inDashParams", kFloat4_GrVertexAttribType);
275 }
276
277 ~ButtCapDashedCircleGeometryProcessor() override {}
278
279 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
280
281 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
282 GLSLProcessor::GenKey(*this, caps, b);
283 }
284
285 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
286 return new GLSLProcessor();
287 }
288
289private:
290 class GLSLProcessor : public GrGLSLGeometryProcessor {
291 public:
292 GLSLProcessor() {}
293
294 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
295 const ButtCapDashedCircleGeometryProcessor& bcscgp =
296 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
297 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
298 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
299 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
300 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
301
302 // emit attributes
303 varyingHandler->emitAttributes(bcscgp);
304 fragBuilder->codeAppend("float4 circleEdge;");
305 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
306
307 fragBuilder->codeAppend("float4 dashParams;");
308 varyingHandler->addPassThroughAttribute(
309 bcscgp.fInDashParams, "dashParams",
310 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
311 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
312 varyingHandler->addVarying("wrapDashes", &wrapDashes,
313 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
314 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
315 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
316 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
317 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams->fName);
318 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
319 // x = length of on interval, y = length of on + off.
320 // There are two other parameters in dashParams.zw:
321 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
322 // Each interval has a "corresponding" dash which may be shifted partially or
323 // fully out of its interval by the phase. So there may be up to two "visual"
324 // dashes in an interval.
325 // When computing coverage in an interval we look at three dashes. These are the
326 // "corresponding" dashes from the current, previous, and next intervals. Any of these
327 // may be phase shifted into our interval or even when phase=0 they may be within half a
328 // pixel distance of a pixel center in the interval.
329 // When in the first interval we need to check the dash from the last interval. And
330 // similarly when in the last interval we need to check the dash from the first
331 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
332 // We compute the dash begin/end angles in the vertex shader and apply them in the
333 // fragment shader when we detect we're in the first/last interval.
334 vertBuilder->codeAppend(R"(
335 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
336 // to the fragment shader as a varying.
337 float4 wrapDashes;
338 half lastIntervalLength = mod(6.28318530718, dashParams.y);
339 // We can happen to be perfectly divisible.
340 if (0 == lastIntervalLength) {
341 lastIntervalLength = dashParams.y;
342 }
343 // Let 'l' be the last interval before reaching 2 pi.
344 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
345 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
346 // interval.
347 half offset = 0;
348 if (-dashParams.w >= lastIntervalLength) {
349 offset = -dashParams.y;
350 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
351 offset = dashParams.y;
352 }
353 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
354 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
355 // min.
356 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
357
358 // Based on the phase determine whether the -1st, 0th, or 1st interval's
359 // "corresponding" dash appears in the 0th interval and is closest to l.
360 offset = 0;
361 if (dashParams.w >= dashParams.x) {
362 offset = dashParams.y;
363 } else if (-dashParams.w > dashParams.y - dashParams.x) {
364 offset = -dashParams.y;
365 }
366 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
367 wrapDashes.w = wrapDashes.z + dashParams.x;
368 // The start of the dash we're considering may be clipped by the start of the
369 // circle.
370 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
371 )");
372 vertBuilder->codeAppendf("%s = wrapDashes;", wrapDashes.vsOut());
373 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
374 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
375 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
376
377 // setup pass through color
378 varyingHandler->addPassThroughAttribute(
379 bcscgp.fInColor, args.fOutputColor,
380 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
381
382 // Setup position
383 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition->fName);
384
385 // emit transforms
386 this->emitTransforms(vertBuilder,
387 varyingHandler,
388 uniformHandler,
389 bcscgp.fInPosition->asShaderVar(),
390 bcscgp.fLocalMatrix,
391 args.fFPCoordTransformHandler);
392 GrShaderVar fnArgs[] = {
393 GrShaderVar("angleToEdge", kFloat_GrSLType),
394 GrShaderVar("diameter", kFloat_GrSLType),
395 };
396 SkString fnName;
397 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
398 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
399 float linearDist;
400 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
401 linearDist = diameter * sin(angleToEdge / 2);
402 return clamp(linearDist + 0.5, 0, 1);
403 )",
404 &fnName);
405 fragBuilder->codeAppend(R"(
406 float d = length(circleEdge.xy) * circleEdge.z;
407
408 // Compute coverage from outer/inner edges of the stroke.
409 half distanceToOuterEdge = circleEdge.z - d;
410 half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);
411 half distanceToInnerEdge = d - circleEdge.z * circleEdge.w;
412 half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);
413 edgeAlpha *= innerAlpha;
414
415 half angleFromStart = atan(circleEdge.y, circleEdge.x) - dashParams.z;
416 angleFromStart = mod(angleFromStart, 6.28318530718);
417 float x = mod(angleFromStart, dashParams.y);
418 // Convert the radial distance from center to pixel into a diameter.
419 d *= 2;
420 half2 currDash = half2(-dashParams.w, dashParams.x - dashParams.w);
421 half2 nextDash = half2(dashParams.y - dashParams.w,
422 dashParams.y + dashParams.x - dashParams.w);
423 half2 prevDash = half2(-dashParams.y - dashParams.w,
424 -dashParams.y + dashParams.x - dashParams.w);
425 half dashAlpha = 0;
426 )");
427 fragBuilder->codeAppendf(R"(
428 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
429 dashAlpha += %s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d);
430 currDash.y = min(currDash.y, lastIntervalLength);
431 if (nextDash.x >= lastIntervalLength) {
432 // The next dash is outside the 0..2pi range, throw it away
433 nextDash.xy = half2(1000);
434 } else {
435 // Clip the end of the next dash to the end of the circle
436 nextDash.y = min(nextDash.y, lastIntervalLength);
437 }
438 }
439 )", fnName.c_str(), fnName.c_str());
440 fragBuilder->codeAppendf(R"(
441 if (angleFromStart - x - dashParams.y < -0.01) {
442 dashAlpha += %s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d);
443 currDash.x = max(currDash.x, 0);
444 if (prevDash.y <= 0) {
445 // The previous dash is outside the 0..2pi range, throw it away
446 prevDash.xy = half2(1000);
447 } else {
448 // Clip the start previous dash to the start of the circle
449 prevDash.x = max(prevDash.x, 0);
450 }
451 }
452 )", fnName.c_str(), fnName.c_str());
453 fragBuilder->codeAppendf(R"(
454 dashAlpha += %s(x - currDash.x, d) * %s(currDash.y - x, d);
455 dashAlpha += %s(x - nextDash.x, d) * %s(nextDash.y - x, d);
456 dashAlpha += %s(x - prevDash.x, d) * %s(prevDash.y - x, d);
457 dashAlpha = min(dashAlpha, 1);
458 edgeAlpha *= dashAlpha;
459 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
460 fnName.c_str());
461 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
462 }
463
464 static void GenKey(const GrGeometryProcessor& gp,
465 const GrShaderCaps&,
466 GrProcessorKeyBuilder* b) {
467 const ButtCapDashedCircleGeometryProcessor& bcscgp =
468 gp.cast<ButtCapDashedCircleGeometryProcessor>();
469 b->add32(bcscgp.fLocalMatrix.hasPerspective());
470 }
471
472 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
473 FPCoordTransformIter&& transformIter) override {
474 this->setTransformDataHelper(
475 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
476 &transformIter);
477 }
478
479 private:
480 typedef GrGLSLGeometryProcessor INHERITED;
481 };
482
483 SkMatrix fLocalMatrix;
484 const Attribute* fInPosition;
485 const Attribute* fInColor;
486 const Attribute* fInCircleEdge;
487 const Attribute* fInDashParams;
488
489 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
490
491 typedef GrGeometryProcessor INHERITED;
492};
493
494#if GR_TEST_UTILS
495sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
496 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
497 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(matrix));
498}
499#endif
500
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000501///////////////////////////////////////////////////////////////////////////////
502
503/**
504 * 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 +0000505 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
506 * in both x and y directions.
507 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000508 * 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 +0000509 */
510
bsalomoncdaa97b2016-03-08 08:30:14 -0800511class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000512public:
Brian Salomonea26d6b2018-01-23 20:33:21 +0000513 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
514 : INHERITED(kEllipseGeometryProcessor_ClassID)
515 , fLocalMatrix(localMatrix) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400516 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
517 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
518 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType);
519 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType);
Brian Salomonea26d6b2018-01-23 20:33:21 +0000520 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000521 }
522
Brian Salomond3b65972017-03-22 12:05:03 -0400523 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524
mtklein36352bf2015-03-25 18:17:31 -0700525 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800526
Brian Salomon94efbf52016-11-29 13:43:05 -0500527 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700528 GLSLProcessor::GenKey(*this, caps, b);
529 }
530
Brian Salomon94efbf52016-11-29 13:43:05 -0500531 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700532 return new GLSLProcessor();
533 }
534
535private:
egdaniel57d3b032015-11-13 11:57:27 -0800536 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000537 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800538 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000539
Brian Salomon289e3d82016-12-14 15:52:56 -0500540 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800541 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800542 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800543 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800544 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000545
joshualittabb52a12015-01-13 15:02:10 -0800546 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800547 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800548
Chris Dalton27372882017-12-08 13:34:21 -0700549 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800550 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800551 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700552 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000553
Chris Dalton27372882017-12-08 13:34:21 -0700554 GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800555 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500556 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800557
Chris Dalton60283612018-02-14 13:38:14 -0700558 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700559 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700560 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800561
joshualittabb52a12015-01-13 15:02:10 -0800562 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400563 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800564
565 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800566 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800567 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800568 uniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500569 egp.fInPosition->asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700570 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700571 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800572
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000573 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400574 fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800575 ellipseRadii.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400576 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
577 fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
578 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700579
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000580 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800581 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400582 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
583 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000584
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000585 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800586 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500587 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800588 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500589 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
590 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800591 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
592 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000593 }
594
Brian Salomonea26d6b2018-01-23 20:33:21 +0000595 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000596 }
597
robertphillips46d36f02015-01-18 08:14:14 -0800598 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500599 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700600 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800601 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
602 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700603 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700604 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605 }
606
bsalomona624bf32016-09-20 09:12:47 -0700607 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
608 FPCoordTransformIter&& transformIter) override {
609 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
610 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700611 }
612
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000613 private:
egdaniele659a582015-11-13 09:55:43 -0800614 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000615 };
616
joshualitt71c92602015-01-14 08:12:47 -0800617 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800618 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800619 const Attribute* fInEllipseOffset;
620 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700621 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000622 bool fStroke;
623
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400624 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000625
joshualitt249af152014-09-15 11:41:13 -0700626 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000627};
628
bsalomoncdaa97b2016-03-08 08:30:14 -0800629GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000630
Hal Canary6f6961e2017-01-31 13:50:44 -0500631#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700632sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000633 return sk_sp<GrGeometryProcessor>(
634 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000635}
Hal Canary6f6961e2017-01-31 13:50:44 -0500636#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000637
638///////////////////////////////////////////////////////////////////////////////
639
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000640/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000641 * 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 +0000642 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
643 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
644 * using differentials.
645 *
646 * The result is device-independent and can be used with any affine matrix.
647 */
648
bsalomoncdaa97b2016-03-08 08:30:14 -0800649enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000650
bsalomoncdaa97b2016-03-08 08:30:14 -0800651class DIEllipseGeometryProcessor : public GrGeometryProcessor {
652public:
Brian Salomonea26d6b2018-01-23 20:33:21 +0000653 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400654 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000655 , fViewMatrix(viewMatrix) {
Ethan Nicholasfa7ee242017-09-25 09:52:04 -0400656 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
657 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
658 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType);
659 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType);
Brian Salomonea26d6b2018-01-23 20:33:21 +0000660 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000661 }
662
Brian Salomond3b65972017-03-22 12:05:03 -0400663 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000664
mtklein36352bf2015-03-25 18:17:31 -0700665 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000666
Brian Salomon94efbf52016-11-29 13:43:05 -0500667 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700668 GLSLProcessor::GenKey(*this, caps, b);
669 }
halcanary9d524f22016-03-29 09:03:52 -0700670
Brian Salomon94efbf52016-11-29 13:43:05 -0500671 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700672 return new GLSLProcessor();
673 }
674
675private:
egdaniel57d3b032015-11-13 11:57:27 -0800676 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000677 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500678 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000679
joshualitt465283c2015-09-11 08:19:35 -0700680 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800681 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800682 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800683 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800684 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000685
joshualittabb52a12015-01-13 15:02:10 -0800686 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800687 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800688
Chris Dalton27372882017-12-08 13:34:21 -0700689 GrGLSLVarying offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800690 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500691 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700692
Chris Dalton27372882017-12-08 13:34:21 -0700693 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800694 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500695 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800696
Chris Dalton60283612018-02-14 13:38:14 -0700697 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700698 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800699
joshualittabb52a12015-01-13 15:02:10 -0800700 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400701 this->writeOutputPosition(vertBuilder,
702 uniformHandler,
703 gpArgs,
704 diegp.fInPosition->fName,
705 diegp.fViewMatrix,
706 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800707
708 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800709 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800710 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800711 uniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500712 diegp.fInPosition->asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700713 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800714
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000715 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400716 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
717 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
718 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
719 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500720 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400721 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
722 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500723 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000724
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400725 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000726 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800727 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400728 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800729 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000730 // can probably do this with one step
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400731 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800732 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000733 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400734 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000735 }
736
737 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800738 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800739 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
740 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
741 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
742 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500743 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400744 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
745 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500746 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800747 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
748 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000749 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000750
751 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000752 }
753
robertphillips46d36f02015-01-18 08:14:14 -0800754 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500755 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700756 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800757 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
758 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700759 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700760 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000761 }
762
bsalomona624bf32016-09-20 09:12:47 -0700763 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
764 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800765 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700766
bsalomon31df31c2016-08-17 09:00:24 -0700767 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
768 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700769 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800770 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700771 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
772 }
bsalomona624bf32016-09-20 09:12:47 -0700773 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000774 }
775
776 private:
joshualitt5559ca22015-05-21 15:50:36 -0700777 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700778 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800779
egdaniele659a582015-11-13 09:55:43 -0800780 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000781 };
782
joshualitt71c92602015-01-14 08:12:47 -0800783 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800784 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800785 const Attribute* fInEllipseOffsets0;
786 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500787 SkMatrix fViewMatrix;
788 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000789
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400790 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000791
joshualitt249af152014-09-15 11:41:13 -0700792 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793};
794
bsalomoncdaa97b2016-03-08 08:30:14 -0800795GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000796
Hal Canary6f6961e2017-01-31 13:50:44 -0500797#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700798sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500799 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Salomonea26d6b2018-01-23 20:33:21 +0000800 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000801}
Hal Canary6f6961e2017-01-31 13:50:44 -0500802#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000803
804///////////////////////////////////////////////////////////////////////////////
805
jvanverth6ca48822016-10-07 06:57:32 -0700806// We have two possible cases for geometry for a circle:
807
808// In the case of a normal fill, we draw geometry for the circle as an octagon.
809static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500810 // enter the octagon
811 // clang-format off
812 0, 1, 8, 1, 2, 8,
813 2, 3, 8, 3, 4, 8,
814 4, 5, 8, 5, 6, 8,
815 6, 7, 8, 7, 0, 8
816 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700817};
818
819// For stroked circles, we use two nested octagons.
820static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500821 // enter the octagon
822 // clang-format off
823 0, 1, 9, 0, 9, 8,
824 1, 2, 10, 1, 10, 9,
825 2, 3, 11, 2, 11, 10,
826 3, 4, 12, 3, 12, 11,
827 4, 5, 13, 4, 13, 12,
828 5, 6, 14, 5, 14, 13,
829 6, 7, 15, 6, 15, 14,
830 7, 0, 8, 7, 8, 15,
831 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700832};
833
Brian Salomon289e3d82016-12-14 15:52:56 -0500834
jvanverth6ca48822016-10-07 06:57:32 -0700835static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
836static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
837static const int kVertsPerStrokeCircle = 16;
838static const int kVertsPerFillCircle = 9;
839
840static int circle_type_to_vert_count(bool stroked) {
841 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
842}
843
844static int circle_type_to_index_count(bool stroked) {
845 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
846}
847
848static const uint16_t* circle_type_to_indices(bool stroked) {
849 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
850}
851
852///////////////////////////////////////////////////////////////////////////////
853
Brian Salomon05441c42017-05-15 16:45:49 -0400854class CircleOp final : public GrMeshDrawOp {
855private:
856 using Helper = GrSimpleMeshDrawOpHelper;
857
joshualitt76e7fb62015-02-11 08:52:27 -0800858public:
Brian Salomon25a88092016-12-01 09:36:50 -0500859 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700860
bsalomon4f3a0ca2016-08-22 13:14:26 -0700861 /** Optional extra params to render a partial arc rather than a full circle. */
862 struct ArcParams {
863 SkScalar fStartAngleRadians;
864 SkScalar fSweepAngleRadians;
865 bool fUseCenter;
866 };
Brian Salomon05441c42017-05-15 16:45:49 -0400867
Brian Salomonea26d6b2018-01-23 20:33:21 +0000868 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400869 SkPoint center, SkScalar radius, const GrStyle& style,
870 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700871 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700872 if (style.hasPathEffect()) {
873 return nullptr;
874 }
Brian Salomon05441c42017-05-15 16:45:49 -0400875 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700876 SkStrokeRec::Style recStyle = stroke.getStyle();
877 if (arcParams) {
878 // Arc support depends on the style.
879 switch (recStyle) {
880 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500881 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700882 return nullptr;
883 case SkStrokeRec::kFill_Style:
884 // This supports all fills.
885 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400886 case SkStrokeRec::kStroke_Style:
887 // Strokes that don't use the center point are supported with butt and round
888 // caps.
889 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
890 return nullptr;
891 }
892 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700893 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400894 // Hairline only supports butt cap. Round caps could be emulated by slightly
895 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700896 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
897 return nullptr;
898 }
899 break;
900 }
901 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000902 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
903 arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400904 }
905
Brian Salomonea26d6b2018-01-23 20:33:21 +0000906 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400907 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000908 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400909 const SkStrokeRec& stroke = style.strokeRec();
910 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700911
Brian Salomon45c92202018-04-10 10:53:58 -0400912 fRoundCaps = false;
913
bsalomon4b4a7cc2016-07-08 04:42:54 -0700914 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700915 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700916 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800917
Brian Salomon289e3d82016-12-14 15:52:56 -0500918 bool isStrokeOnly =
919 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700920 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700921
jvanverth6ca48822016-10-07 06:57:32 -0700922 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700923 SkScalar outerRadius = radius;
924 SkScalar halfWidth = 0;
925 if (hasStroke) {
926 if (SkScalarNearlyZero(strokeWidth)) {
927 halfWidth = SK_ScalarHalf;
928 } else {
929 halfWidth = SkScalarHalf(strokeWidth);
930 }
931
932 outerRadius += halfWidth;
933 if (isStrokeOnly) {
934 innerRadius = radius - halfWidth;
935 }
936 }
937
938 // The radii are outset for two reasons. First, it allows the shader to simply perform
939 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
940 // Second, the outer radius is used to compute the verts of the bounding box that is
941 // rendered and the outset ensures the box will cover all partially covered by the circle.
942 outerRadius += SK_ScalarHalf;
943 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700944 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400945 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700946
bsalomon4f3a0ca2016-08-22 13:14:26 -0700947 // This makes every point fully inside the intersection plane.
948 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
949 // This makes every point fully outside the union plane.
950 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400951 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700952 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
953 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700954 if (arcParams) {
955 // The shader operates in a space where the circle is translated to be centered at the
956 // origin. Here we compute points on the unit circle at the starting and ending angles.
957 SkPoint startPoint, stopPoint;
958 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
959 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
960 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400961
962 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
963 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
964 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
965 startPoint.normalize();
966 stopPoint.normalize();
967
968 // If the matrix included scale (on one axis) we need to swap our start and end points
969 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
970 SkTSwap(startPoint, stopPoint);
971 }
972
Brian Salomon45c92202018-04-10 10:53:58 -0400973 fRoundCaps = style.strokeRec().getWidth() > 0 &&
974 style.strokeRec().getCap() == SkPaint::kRound_Cap;
975 SkPoint roundCaps[2];
976 if (fRoundCaps) {
977 // Compute the cap center points in the normalized space.
978 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
979 roundCaps[0] = startPoint * midRadius;
980 roundCaps[1] = stopPoint * midRadius;
981 } else {
982 roundCaps[0] = kUnusedRoundCaps[0];
983 roundCaps[1] = kUnusedRoundCaps[1];
984 }
985
bsalomon4f3a0ca2016-08-22 13:14:26 -0700986 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -0400987 // radial lines. We treat round caps the same way, but tack coverage of circles at the
988 // center of the butts.
989 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700990 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -0400991 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -0700992 // case.
Brian Salomon45c92202018-04-10 10:53:58 -0400993 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
994 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
995 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700996 if (useCenter) {
997 SkVector norm0 = {startPoint.fY, -startPoint.fX};
998 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
999 if (arcParams->fSweepAngleRadians > 0) {
1000 norm0.negate();
1001 } else {
1002 norm1.negate();
1003 }
Brian Salomon05441c42017-05-15 16:45:49 -04001004 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001005 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001006 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001007 color,
1008 innerRadius,
1009 outerRadius,
1010 {norm0.fX, norm0.fY, 0.5f},
1011 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1012 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001013 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001014 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001015 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001016 fClipPlaneIsect = false;
1017 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001018 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001019 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 color,
1021 innerRadius,
1022 outerRadius,
1023 {norm0.fX, norm0.fY, 0.5f},
1024 {norm1.fX, norm1.fY, 0.5f},
1025 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001026 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001027 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001028 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001029 fClipPlaneIsect = true;
1030 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 }
1032 } else {
1033 // We clip to a secant of the original circle.
1034 startPoint.scale(radius);
1035 stopPoint.scale(radius);
1036 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1037 norm.normalize();
1038 if (arcParams->fSweepAngleRadians > 0) {
1039 norm.negate();
1040 }
1041 SkScalar d = -norm.dot(startPoint) + 0.5f;
1042
Brian Salomon05441c42017-05-15 16:45:49 -04001043 fCircles.emplace_back(
1044 Circle{color,
1045 innerRadius,
1046 outerRadius,
1047 {norm.fX, norm.fY, d},
1048 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1049 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001050 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001051 devBounds,
1052 stroked});
1053 fClipPlane = true;
1054 fClipPlaneIsect = false;
1055 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001056 }
1057 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001058 fCircles.emplace_back(
1059 Circle{color,
1060 innerRadius,
1061 outerRadius,
1062 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1063 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1064 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001065 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001066 devBounds,
1067 stroked});
1068 fClipPlane = false;
1069 fClipPlaneIsect = false;
1070 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001071 }
bsalomon88cf17d2016-07-08 06:40:56 -07001072 // Use the original radius and stroke radius for the bounds so that it does not include the
1073 // AA bloat.
1074 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001075 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001076 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1077 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001078 fVertCount = circle_type_to_vert_count(stroked);
1079 fIndexCount = circle_type_to_index_count(stroked);
1080 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001081 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001082
Brian Salomon289e3d82016-12-14 15:52:56 -05001083 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001084
Robert Phillipsf1748f52017-09-14 14:11:24 -04001085 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001086 fHelper.visitProxies(func);
1087 }
1088
robertphillipse004bfc2015-11-16 09:06:59 -08001089 SkString dumpInfo() const override {
1090 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001091 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001092 string.appendf(
1093 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1094 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001095 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1096 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1097 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001098 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001099 string += fHelper.dumpInfo();
1100 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001101 return string;
1102 }
1103
Brian Osman9a725dd2017-09-20 09:53:22 -04001104 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1105 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001106 GrColor* color = &fCircles.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001107 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1108 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001109 }
1110
1111 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1112
bsalomone46f9fe2015-08-18 06:05:14 -07001113private:
Brian Salomon91326c32017-08-09 16:02:19 -04001114 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001115 SkMatrix localMatrix;
1116 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001117 return;
1118 }
1119
1120 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001121 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Salomon45c92202018-04-10 10:53:58 -04001122 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001123
1124 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001125 SkPoint fPos;
1126 GrColor fColor;
1127 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 SkScalar fOuterRadius;
1129 SkScalar fInnerRadius;
1130 // These planes may or may not be present in the vertex buffer.
1131 SkScalar fHalfPlanes[3][3];
1132 };
joshualitt76e7fb62015-02-11 08:52:27 -08001133
Brian Salomon45c92202018-04-10 10:53:58 -04001134 int numPlanes = (int)fClipPlane + fClipPlaneIsect + fClipPlaneUnion;
1135 auto vertexCapCenters = [numPlanes](CircleVertex* v) {
1136 return (void*)(v->fHalfPlanes + numPlanes);
1137 };
joshualitt76e7fb62015-02-11 08:52:27 -08001138 size_t vertexStride = gp->getVertexStride();
Brian Salomon45c92202018-04-10 10:53:58 -04001139 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
1140 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
1141 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
1142 (fRoundCaps ? 2 * sizeof(SkPoint) : 0));
jvanverth6ca48822016-10-07 06:57:32 -07001143
1144 const GrBuffer* vertexBuffer;
1145 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -05001146 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1147 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001148 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -07001149 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001150 return;
1151 }
1152
jvanverth6ca48822016-10-07 06:57:32 -07001153 const GrBuffer* indexBuffer = nullptr;
1154 int firstIndex = 0;
1155 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1156 if (!indices) {
1157 SkDebugf("Could not allocate indices\n");
1158 return;
1159 }
1160
1161 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001162 for (const auto& circle : fCircles) {
1163 SkScalar innerRadius = circle.fInnerRadius;
1164 SkScalar outerRadius = circle.fOuterRadius;
1165 GrColor color = circle.fColor;
1166 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001167
Brian Salomon289e3d82016-12-14 15:52:56 -05001168 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
1169 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
1170 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
1171 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
1172 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
1173 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
1174 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
1175 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001176
1177 // The inner radius in the vertex data must be specified in normalized space.
1178 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001179
1180 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001181 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -07001182 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -05001183
Brian Salomon289e3d82016-12-14 15:52:56 -05001184 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001185 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001186 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001187 v0->fOuterRadius = outerRadius;
1188 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001189
Brian Salomon289e3d82016-12-14 15:52:56 -05001190 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001191 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001192 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001193 v1->fOuterRadius = outerRadius;
1194 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001195
Brian Salomon289e3d82016-12-14 15:52:56 -05001196 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001197 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001198 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001199 v2->fOuterRadius = outerRadius;
1200 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001201
Brian Salomon289e3d82016-12-14 15:52:56 -05001202 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001203 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001204 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001205 v3->fOuterRadius = outerRadius;
1206 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001207
Brian Salomon289e3d82016-12-14 15:52:56 -05001208 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001209 v4->fColor = color;
1210 v4->fOffset = SkPoint::Make(octOffset, 1);
1211 v4->fOuterRadius = outerRadius;
1212 v4->fInnerRadius = innerRadius;
1213
Brian Salomon289e3d82016-12-14 15:52:56 -05001214 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001215 v5->fColor = color;
1216 v5->fOffset = SkPoint::Make(-octOffset, 1);
1217 v5->fOuterRadius = outerRadius;
1218 v5->fInnerRadius = innerRadius;
1219
Brian Salomon289e3d82016-12-14 15:52:56 -05001220 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001221 v6->fColor = color;
1222 v6->fOffset = SkPoint::Make(-1, octOffset);
1223 v6->fOuterRadius = outerRadius;
1224 v6->fInnerRadius = innerRadius;
1225
Brian Salomon289e3d82016-12-14 15:52:56 -05001226 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001227 v7->fColor = color;
1228 v7->fOffset = SkPoint::Make(-1, -octOffset);
1229 v7->fOuterRadius = outerRadius;
1230 v7->fInnerRadius = innerRadius;
1231
bsalomon4f3a0ca2016-08-22 13:14:26 -07001232 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001233 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1234 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1235 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1236 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1237 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1238 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1239 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1240 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001241 }
1242 int unionIdx = 1;
1243 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001244 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1245 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1246 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1247 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1248 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1249 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1250 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1251 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001252 unionIdx = 2;
1253 }
1254 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001255 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1256 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1257 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1258 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1259 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1260 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1261 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1262 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001263 }
Brian Salomon45c92202018-04-10 10:53:58 -04001264 if (fRoundCaps) {
1265 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1266 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1267 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1268 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1269 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1270 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1271 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1272 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1273 }
jvanverth6ca48822016-10-07 06:57:32 -07001274
Brian Salomon05441c42017-05-15 16:45:49 -04001275 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001276 // compute the inner ring
1277 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1278 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
1279 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
1280 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
1281 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
1282 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
1283 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
1284 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
1285
1286 // cosine and sine of pi/8
1287 SkScalar c = 0.923579533f;
1288 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -04001289 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001290
Brian Salomon289e3d82016-12-14 15:52:56 -05001291 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001292 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001293 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001294 v0->fOuterRadius = outerRadius;
1295 v0->fInnerRadius = innerRadius;
1296
Brian Salomon289e3d82016-12-14 15:52:56 -05001297 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001298 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001299 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001300 v1->fOuterRadius = outerRadius;
1301 v1->fInnerRadius = innerRadius;
1302
Brian Salomon289e3d82016-12-14 15:52:56 -05001303 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001304 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001305 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001306 v2->fOuterRadius = outerRadius;
1307 v2->fInnerRadius = innerRadius;
1308
Brian Salomon289e3d82016-12-14 15:52:56 -05001309 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001310 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001311 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001312 v3->fOuterRadius = outerRadius;
1313 v3->fInnerRadius = innerRadius;
1314
Brian Salomon289e3d82016-12-14 15:52:56 -05001315 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001316 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001317 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001318 v4->fOuterRadius = outerRadius;
1319 v4->fInnerRadius = innerRadius;
1320
Brian Salomon289e3d82016-12-14 15:52:56 -05001321 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001322 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001323 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001324 v5->fOuterRadius = outerRadius;
1325 v5->fInnerRadius = innerRadius;
1326
Brian Salomon289e3d82016-12-14 15:52:56 -05001327 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001328 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001329 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001330 v6->fOuterRadius = outerRadius;
1331 v6->fInnerRadius = innerRadius;
1332
Brian Salomon289e3d82016-12-14 15:52:56 -05001333 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001334 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001335 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001336 v7->fOuterRadius = outerRadius;
1337 v7->fInnerRadius = innerRadius;
1338
1339 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001340 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1341 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1342 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1343 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1344 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1345 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1346 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1347 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001348 }
1349 int unionIdx = 1;
1350 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001351 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1352 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1353 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1354 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1355 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1356 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1357 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1358 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001359 unionIdx = 2;
1360 }
1361 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001362 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1363 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1364 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1365 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1366 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1367 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1368 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1369 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001370 }
Brian Salomon45c92202018-04-10 10:53:58 -04001371 if (fRoundCaps) {
1372 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1373 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1374 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1375 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1376 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1377 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1378 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1379 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1380 }
jvanverth6ca48822016-10-07 06:57:32 -07001381 } else {
1382 // filled
1383 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1384 v8->fPos = center;
1385 v8->fColor = color;
1386 v8->fOffset = SkPoint::Make(0, 0);
1387 v8->fOuterRadius = outerRadius;
1388 v8->fInnerRadius = innerRadius;
1389 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001390 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001391 }
1392 int unionIdx = 1;
1393 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001394 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001395 unionIdx = 2;
1396 }
1397 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001398 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001399 }
Brian Salomon45c92202018-04-10 10:53:58 -04001400 SkASSERT(!fRoundCaps);
jvanverth6ca48822016-10-07 06:57:32 -07001401 }
1402
Brian Salomon05441c42017-05-15 16:45:49 -04001403 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1404 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001405 for (int i = 0; i < primIndexCount; ++i) {
1406 *indices++ = primIndices[i] + currStartVertex;
1407 }
1408
Brian Salomon05441c42017-05-15 16:45:49 -04001409 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1410 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001411 }
jvanverth6ca48822016-10-07 06:57:32 -07001412
Chris Dalton3809bab2017-06-13 10:55:06 -06001413 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001414 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1415 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001416 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001417 }
1418
Brian Salomon25a88092016-12-01 09:36:50 -05001419 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001420 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001421
1422 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001423 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001424 return false;
1425 }
1426
Brian Salomon05441c42017-05-15 16:45:49 -04001427 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001428 return false;
1429 }
1430
Brian Salomon05441c42017-05-15 16:45:49 -04001431 if (fHelper.usesLocalCoords() &&
1432 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001433 return false;
1434 }
1435
Brian Salomon289e3d82016-12-14 15:52:56 -05001436 // Because we've set up the ops that don't use the planes with noop values
1437 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001438 fClipPlane |= that->fClipPlane;
1439 fClipPlaneIsect |= that->fClipPlaneIsect;
1440 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001441 fRoundCaps |= that->fRoundCaps;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001442
Brian Salomon05441c42017-05-15 16:45:49 -04001443 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001444 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001445 fVertCount += that->fVertCount;
1446 fIndexCount += that->fIndexCount;
1447 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001448 return true;
1449 }
1450
Brian Salomon05441c42017-05-15 16:45:49 -04001451 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001452 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001453 SkScalar fInnerRadius;
1454 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001455 SkScalar fClipPlane[3];
1456 SkScalar fIsectPlane[3];
1457 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001458 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001459 SkRect fDevBounds;
1460 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001461 };
1462
Brian Salomon289e3d82016-12-14 15:52:56 -05001463 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001464 Helper fHelper;
1465 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001466 int fVertCount;
1467 int fIndexCount;
1468 bool fAllFill;
1469 bool fClipPlane;
1470 bool fClipPlaneIsect;
1471 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001472 bool fRoundCaps;
reed1b55a962015-09-17 20:16:13 -07001473
Brian Salomon05441c42017-05-15 16:45:49 -04001474 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001475};
1476
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001477class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1478private:
1479 using Helper = GrSimpleMeshDrawOpHelper;
1480
1481public:
1482 DEFINE_OP_CLASS_ID
1483
1484 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1485 SkPoint center, SkScalar radius, SkScalar strokeWidth,
1486 SkScalar startAngle, SkScalar onAngle, SkScalar offAngle,
1487 SkScalar phaseAngle) {
1488 SkASSERT(circle_stays_circle(viewMatrix));
1489 SkASSERT(strokeWidth < 2 * radius);
1490 return Helper::FactoryHelper<ButtCapDashedCircleOp>(std::move(paint), viewMatrix, center,
1491 radius, strokeWidth, startAngle,
1492 onAngle, offAngle, phaseAngle);
1493 }
1494
1495 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color,
1496 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1497 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1498 SkScalar offAngle, SkScalar phaseAngle)
1499 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1500 SkASSERT(circle_stays_circle(viewMatrix));
1501 viewMatrix.mapPoints(&center, 1);
1502 radius = viewMatrix.mapRadius(radius);
1503 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1504
1505 // Determine the angle where the circle starts in device space and whether its orientation
1506 // has been reversed.
1507 SkVector start;
1508 bool reflection;
1509 if (!startAngle) {
1510 start = {1, 0};
1511 } else {
1512 start.fY = SkScalarSinCos(startAngle, &start.fX);
1513 }
1514 viewMatrix.mapVectors(&start, 1);
1515 startAngle = SkScalarATan2(start.fY, start.fX);
1516 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1517 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1518
1519 auto totalAngle = onAngle + offAngle;
1520 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1521
1522 SkScalar halfWidth = 0;
1523 if (SkScalarNearlyZero(strokeWidth)) {
1524 halfWidth = SK_ScalarHalf;
1525 } else {
1526 halfWidth = SkScalarHalf(strokeWidth);
1527 }
1528
1529 SkScalar outerRadius = radius + halfWidth;
1530 SkScalar innerRadius = radius - halfWidth;
1531
1532 // The radii are outset for two reasons. First, it allows the shader to simply perform
1533 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1534 // Second, the outer radius is used to compute the verts of the bounding box that is
1535 // rendered and the outset ensures the box will cover all partially covered by the circle.
1536 outerRadius += SK_ScalarHalf;
1537 innerRadius -= SK_ScalarHalf;
1538 fViewMatrixIfUsingLocalCoords = viewMatrix;
1539
1540 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1541 center.fX + outerRadius, center.fY + outerRadius);
1542
1543 // We store whether there is a reflection as a negative total angle.
1544 if (reflection) {
1545 totalAngle = -totalAngle;
1546 }
1547 fCircles.push_back(Circle{
1548 color,
1549 outerRadius,
1550 innerRadius,
1551 onAngle,
1552 totalAngle,
1553 startAngle,
1554 phaseAngle,
1555 devBounds
1556 });
1557 // Use the original radius and stroke radius for the bounds so that it does not include the
1558 // AA bloat.
1559 radius += halfWidth;
1560 this->setBounds(
1561 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1562 HasAABloat::kYes, IsZeroArea::kNo);
1563 fVertCount = circle_type_to_vert_count(true);
1564 fIndexCount = circle_type_to_index_count(true);
1565 }
1566
1567 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1568
1569 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); }
1570
1571 SkString dumpInfo() const override {
1572 SkString string;
1573 for (int i = 0; i < fCircles.count(); ++i) {
1574 string.appendf(
1575 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1576 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1577 "Phase: %.2f\n",
1578 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1579 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1580 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle,
1581 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle);
1582 }
1583 string += fHelper.dumpInfo();
1584 string += INHERITED::dumpInfo();
1585 return string;
1586 }
1587
1588 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1589 GrPixelConfigIsClamped dstIsClamped) override {
1590 GrColor* color = &fCircles.front().fColor;
1591 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1592 GrProcessorAnalysisCoverage::kSingleChannel, color);
1593 }
1594
1595 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1596
1597private:
1598 void onPrepareDraws(Target* target) override {
1599 SkMatrix localMatrix;
1600 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1601 return;
1602 }
1603
1604 // Setup geometry processor
1605 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix));
1606
1607 struct CircleVertex {
1608 SkPoint fPos;
1609 GrColor fColor;
1610 SkPoint fOffset;
1611 SkScalar fOuterRadius;
1612 SkScalar fInnerRadius;
1613 SkScalar fOnAngle;
1614 SkScalar fTotalAngle;
1615 SkScalar fStartAngle;
1616 SkScalar fPhaseAngle;
1617 };
1618
1619 size_t vertexStride = gp->getVertexStride();
1620 SkASSERT(vertexStride == sizeof(CircleVertex));
1621
1622 const GrBuffer* vertexBuffer;
1623 int firstVertex;
1624 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1625 &firstVertex);
1626 if (!vertices) {
1627 SkDebugf("Could not allocate vertices\n");
1628 return;
1629 }
1630
1631 const GrBuffer* indexBuffer = nullptr;
1632 int firstIndex = 0;
1633 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1634 if (!indices) {
1635 SkDebugf("Could not allocate indices\n");
1636 return;
1637 }
1638
1639 int currStartVertex = 0;
1640 for (const auto& circle : fCircles) {
1641 // The inner radius in the vertex data must be specified in normalized space so that
1642 // length() can be called with smaller values to avoid precision issues with half
1643 // floats.
1644 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1645 const SkRect& bounds = circle.fDevBounds;
1646 bool reflect = false;
1647 SkScalar totalAngle = circle.fTotalAngle;
1648 if (totalAngle < 0) {
1649 reflect = true;
1650 totalAngle = -totalAngle;
1651 }
1652
1653 // The bounding geometry for the circle is composed of an outer bounding octagon and
1654 // an inner bounded octagon.
1655
1656 // Initializes the attributes that are the same at each vertex. Also applies reflection.
1657 auto init_const_attrs_and_reflect = [&](CircleVertex* v) {
1658 v->fColor = circle.fColor;
1659 v->fOuterRadius = circle.fOuterRadius;
1660 v->fInnerRadius = normInnerRadius;
1661 v->fOnAngle = circle.fOnAngle;
1662 v->fTotalAngle = totalAngle;
1663 v->fStartAngle = circle.fStartAngle;
1664 v->fPhaseAngle = circle.fPhaseAngle;
1665 if (reflect) {
1666 v->fStartAngle = -v->fStartAngle;
1667 v->fOffset.fY = -v->fOffset.fY;
1668 }
1669 };
1670
1671 // Compute the vertices of the outer octagon.
1672 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1673 SkScalar halfWidth = 0.5f * bounds.width();
1674 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
1675 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * vertexStride);
1676 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
1677 v->fOffset = {x, y};
1678 init_const_attrs_and_reflect(v);
1679 };
1680 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
1681 init_outer_vertex(0, -kOctOffset, -1);
1682 init_outer_vertex(1, kOctOffset, -1);
1683 init_outer_vertex(2, 1, -kOctOffset);
1684 init_outer_vertex(3, 1, kOctOffset);
1685 init_outer_vertex(4, kOctOffset, 1);
1686 init_outer_vertex(5, -kOctOffset, 1);
1687 init_outer_vertex(6, -1, kOctOffset);
1688 init_outer_vertex(7, -1, -kOctOffset);
1689
1690 // Compute the vertices of the inner octagon.
1691 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
1692 CircleVertex* v =
1693 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * vertexStride);
1694 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
1695 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
1696 init_const_attrs_and_reflect(v);
1697 };
1698
1699 // cosine and sine of pi/8
1700 static constexpr SkScalar kCos = 0.923579533f;
1701 static constexpr SkScalar kSin = 0.382683432f;
1702
1703 init_inner_vertex(0, -kSin, -kCos);
1704 init_inner_vertex(1, kSin, -kCos);
1705 init_inner_vertex(2, kCos, -kSin);
1706 init_inner_vertex(3, kCos, kSin);
1707 init_inner_vertex(4, kSin, kCos);
1708 init_inner_vertex(5, -kSin, kCos);
1709 init_inner_vertex(6, -kCos, kSin);
1710 init_inner_vertex(7, -kCos, -kSin);
1711
1712 const uint16_t* primIndices = circle_type_to_indices(true);
1713 const int primIndexCount = circle_type_to_index_count(true);
1714 for (int i = 0; i < primIndexCount; ++i) {
1715 *indices++ = primIndices[i] + currStartVertex;
1716 }
1717
1718 currStartVertex += circle_type_to_vert_count(true);
1719 vertices += circle_type_to_vert_count(true) * vertexStride;
1720 }
1721
1722 GrMesh mesh(GrPrimitiveType::kTriangles);
1723 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1724 mesh.setVertexData(vertexBuffer, firstVertex);
1725 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
1726 }
1727
1728 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1729 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1730
1731 // can only represent 65535 unique vertices with 16-bit indices
1732 if (fVertCount + that->fVertCount > 65536) {
1733 return false;
1734 }
1735
1736 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1737 return false;
1738 }
1739
1740 if (fHelper.usesLocalCoords() &&
1741 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1742 return false;
1743 }
1744
1745 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1746 this->joinBounds(*that);
1747 fVertCount += that->fVertCount;
1748 fIndexCount += that->fIndexCount;
1749 return true;
1750 }
1751
1752 struct Circle {
1753 GrColor fColor;
1754 SkScalar fOuterRadius;
1755 SkScalar fInnerRadius;
1756 SkScalar fOnAngle;
1757 SkScalar fTotalAngle;
1758 SkScalar fStartAngle;
1759 SkScalar fPhaseAngle;
1760 SkRect fDevBounds;
1761 };
1762
1763 SkMatrix fViewMatrixIfUsingLocalCoords;
1764 Helper fHelper;
1765 SkSTArray<1, Circle, true> fCircles;
1766 int fVertCount;
1767 int fIndexCount;
1768
1769 typedef GrMeshDrawOp INHERITED;
1770};
1771
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001772///////////////////////////////////////////////////////////////////////////////
1773
Brian Salomon05441c42017-05-15 16:45:49 -04001774class EllipseOp : public GrMeshDrawOp {
1775private:
1776 using Helper = GrSimpleMeshDrawOpHelper;
1777
1778 struct DeviceSpaceParams {
1779 SkPoint fCenter;
1780 SkScalar fXRadius;
1781 SkScalar fYRadius;
1782 SkScalar fInnerXRadius;
1783 SkScalar fInnerYRadius;
1784 };
1785
joshualitt76e7fb62015-02-11 08:52:27 -08001786public:
Brian Salomon25a88092016-12-01 09:36:50 -05001787 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001788
Brian Salomonea26d6b2018-01-23 20:33:21 +00001789 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04001790 const SkRect& ellipse, const SkStrokeRec& stroke) {
1791 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001792 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001793 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1794 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001795 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1796 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001797 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1798 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1799 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1800 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001801
bsalomon4b4a7cc2016-07-08 04:42:54 -07001802 // do (potentially) anisotropic mapping of stroke
1803 SkVector scaledStroke;
1804 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001805 scaledStroke.fX = SkScalarAbs(
1806 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1807 scaledStroke.fY = SkScalarAbs(
1808 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001809
1810 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001811 bool isStrokeOnly =
1812 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001813 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1814
Brian Salomon05441c42017-05-15 16:45:49 -04001815 params.fInnerXRadius = 0;
1816 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001817 if (hasStroke) {
1818 if (SkScalarNearlyZero(scaledStroke.length())) {
1819 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1820 } else {
1821 scaledStroke.scale(SK_ScalarHalf);
1822 }
1823
1824 // we only handle thick strokes for near-circular ellipses
1825 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001826 (0.5f * params.fXRadius > params.fYRadius ||
1827 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001828 return nullptr;
1829 }
1830
1831 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001832 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1833 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1834 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1835 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001836 return nullptr;
1837 }
1838
1839 // this is legit only if scale & translation (which should be the case at the moment)
1840 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001841 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1842 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001843 }
1844
Brian Salomon05441c42017-05-15 16:45:49 -04001845 params.fXRadius += scaledStroke.fX;
1846 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001847 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00001848 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001849 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001850
Brian Salomonea26d6b2018-01-23 20:33:21 +00001851 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1852 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1853 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001854 SkStrokeRec::Style style = stroke.getStyle();
1855 bool isStrokeOnly =
1856 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001857
Brian Salomon05441c42017-05-15 16:45:49 -04001858 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1859 params.fInnerXRadius, params.fInnerYRadius,
1860 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1861 params.fCenter.fY - params.fYRadius,
1862 params.fCenter.fX + params.fXRadius,
1863 params.fCenter.fY + params.fYRadius)});
1864
1865 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001866
bsalomon4b4a7cc2016-07-08 04:42:54 -07001867 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001868 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869
Brian Salomon05441c42017-05-15 16:45:49 -04001870 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1871 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001872 }
joshualitt76e7fb62015-02-11 08:52:27 -08001873
Brian Salomon289e3d82016-12-14 15:52:56 -05001874 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001875
Robert Phillipsf1748f52017-09-14 14:11:24 -04001876 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001877 fHelper.visitProxies(func);
1878 }
1879
Brian Salomon7c3e7182016-12-01 09:35:30 -05001880 SkString dumpInfo() const override {
1881 SkString string;
1882 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001883 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001884 string.appendf(
1885 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1886 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1887 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1888 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1889 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001890 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001891 string += fHelper.dumpInfo();
1892 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001893 return string;
1894 }
1895
Brian Osman9a725dd2017-09-20 09:53:22 -04001896 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1897 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001898 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001899 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1900 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001901 }
1902
1903 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1904
bsalomone46f9fe2015-08-18 06:05:14 -07001905private:
Brian Salomon91326c32017-08-09 16:02:19 -04001906 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001907 SkMatrix localMatrix;
1908 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001909 return;
1910 }
1911
1912 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00001913 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001914
bsalomonb5238a72015-05-05 07:49:49 -07001915 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001916 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001917 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001918 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1919 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001920 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001921 return;
1922 }
1923
Brian Salomon05441c42017-05-15 16:45:49 -04001924 for (const auto& ellipse : fEllipses) {
1925 GrColor color = ellipse.fColor;
1926 SkScalar xRadius = ellipse.fXRadius;
1927 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001928
1929 // Compute the reciprocals of the radii here to save time in the shader
1930 SkScalar xRadRecip = SkScalarInvert(xRadius);
1931 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001932 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1933 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001934
vjiaoblack977996d2016-06-30 12:20:54 -07001935 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1936 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1937 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1938
joshualitt76e7fb62015-02-11 08:52:27 -08001939 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001940 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001941 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001942 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001943 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1944 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1945
Brian Salomon05441c42017-05-15 16:45:49 -04001946 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001947 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001948 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001949 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1950 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1951
Brian Salomon57caa662017-10-18 12:21:05 +00001952 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001953 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001954 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001955 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1956 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1957
Brian Salomon57caa662017-10-18 12:21:05 +00001958 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001959 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001960 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001961 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1962 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1963
bsalomonb5238a72015-05-05 07:49:49 -07001964 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001965 }
Brian Salomon05441c42017-05-15 16:45:49 -04001966 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001967 }
1968
Brian Salomon25a88092016-12-01 09:36:50 -05001969 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001970 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001971
Brian Salomon05441c42017-05-15 16:45:49 -04001972 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001973 return false;
1974 }
1975
bsalomoncdaa97b2016-03-08 08:30:14 -08001976 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001977 return false;
1978 }
1979
Brian Salomon05441c42017-05-15 16:45:49 -04001980 if (fHelper.usesLocalCoords() &&
1981 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001982 return false;
1983 }
1984
Brian Salomon05441c42017-05-15 16:45:49 -04001985 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001986 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001987 return true;
1988 }
1989
Brian Salomon05441c42017-05-15 16:45:49 -04001990 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001991 GrColor fColor;
1992 SkScalar fXRadius;
1993 SkScalar fYRadius;
1994 SkScalar fInnerXRadius;
1995 SkScalar fInnerYRadius;
1996 SkRect fDevBounds;
1997 };
joshualitt76e7fb62015-02-11 08:52:27 -08001998
Brian Salomon289e3d82016-12-14 15:52:56 -05001999 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002000 Helper fHelper;
2001 bool fStroked;
2002 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002003
Brian Salomon05441c42017-05-15 16:45:49 -04002004 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002005};
2006
joshualitt76e7fb62015-02-11 08:52:27 -08002007/////////////////////////////////////////////////////////////////////////////////////////////////
2008
Brian Salomon05441c42017-05-15 16:45:49 -04002009class DIEllipseOp : public GrMeshDrawOp {
2010private:
2011 using Helper = GrSimpleMeshDrawOpHelper;
2012
2013 struct DeviceSpaceParams {
2014 SkPoint fCenter;
2015 SkScalar fXRadius;
2016 SkScalar fYRadius;
2017 SkScalar fInnerXRadius;
2018 SkScalar fInnerYRadius;
2019 DIEllipseStyle fStyle;
2020 };
2021
joshualitt76e7fb62015-02-11 08:52:27 -08002022public:
Brian Salomon25a88092016-12-01 09:36:50 -05002023 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002024
Brian Salomonea26d6b2018-01-23 20:33:21 +00002025 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002026 const SkRect& ellipse, const SkStrokeRec& stroke) {
2027 DeviceSpaceParams params;
2028 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2029 params.fXRadius = SkScalarHalf(ellipse.width());
2030 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002031
bsalomon4b4a7cc2016-07-08 04:42:54 -07002032 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002033 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2034 ? DIEllipseStyle::kStroke
2035 : (SkStrokeRec::kHairline_Style == style)
2036 ? DIEllipseStyle::kHairline
2037 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002038
Brian Salomon05441c42017-05-15 16:45:49 -04002039 params.fInnerXRadius = 0;
2040 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002041 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2042 SkScalar strokeWidth = stroke.getWidth();
2043
2044 if (SkScalarNearlyZero(strokeWidth)) {
2045 strokeWidth = SK_ScalarHalf;
2046 } else {
2047 strokeWidth *= SK_ScalarHalf;
2048 }
2049
2050 // we only handle thick strokes for near-circular ellipses
2051 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002052 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2053 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002054 return nullptr;
2055 }
2056
2057 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002058 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2059 (strokeWidth * strokeWidth) * params.fXRadius) {
2060 return nullptr;
2061 }
2062 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2063 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002064 return nullptr;
2065 }
2066
2067 // set inner radius (if needed)
2068 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002069 params.fInnerXRadius = params.fXRadius - strokeWidth;
2070 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002071 }
2072
Brian Salomon05441c42017-05-15 16:45:49 -04002073 params.fXRadius += strokeWidth;
2074 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002075 }
Brian Salomon05441c42017-05-15 16:45:49 -04002076 if (DIEllipseStyle::kStroke == params.fStyle &&
2077 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2078 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002079 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002080 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002081 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002082
Brian Salomonea26d6b2018-01-23 20:33:21 +00002083 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
2084 const SkMatrix& viewMatrix)
2085 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002086 // This expands the outer rect so that after CTM we end up with a half-pixel border
2087 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2088 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2089 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2090 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05002091 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2092 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002093
Brian Salomon05441c42017-05-15 16:45:49 -04002094 fEllipses.emplace_back(
2095 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2096 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2097 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2098 params.fCenter.fY - params.fYRadius - geoDy,
2099 params.fCenter.fX + params.fXRadius + geoDx,
2100 params.fCenter.fY + params.fYRadius + geoDy)});
2101 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2102 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002103 }
2104
Brian Salomon289e3d82016-12-14 15:52:56 -05002105 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002106
Robert Phillipsf1748f52017-09-14 14:11:24 -04002107 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002108 fHelper.visitProxies(func);
2109 }
2110
Brian Salomon7c3e7182016-12-01 09:35:30 -05002111 SkString dumpInfo() const override {
2112 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002113 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002114 string.appendf(
2115 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2116 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2117 "GeoDY: %.2f\n",
2118 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
2119 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2120 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002121 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002122 string += fHelper.dumpInfo();
2123 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002124 return string;
2125 }
2126
Brian Osman9a725dd2017-09-20 09:53:22 -04002127 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2128 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002129 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002130 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2131 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002132 }
2133
2134 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2135
bsalomone46f9fe2015-08-18 06:05:14 -07002136private:
Brian Salomon91326c32017-08-09 16:02:19 -04002137 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002138 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002139 sk_sp<GrGeometryProcessor> gp(
Brian Salomonea26d6b2018-01-23 20:33:21 +00002140 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002141
joshualitt76e7fb62015-02-11 08:52:27 -08002142 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08002143 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07002144 QuadHelper helper;
2145 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04002146 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002147 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002148 return;
2149 }
2150
Brian Salomon05441c42017-05-15 16:45:49 -04002151 for (const auto& ellipse : fEllipses) {
2152 GrColor color = ellipse.fColor;
2153 SkScalar xRadius = ellipse.fXRadius;
2154 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002155
Brian Salomon05441c42017-05-15 16:45:49 -04002156 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002157
2158 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002159 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2160 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002161
joshualitt76e7fb62015-02-11 08:52:27 -08002162 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002163 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002164 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002165 verts[0].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002166
Brian Salomon289e3d82016-12-14 15:52:56 -05002167 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002168 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002169 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002170 verts[1].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002171
Brian Salomon57caa662017-10-18 12:21:05 +00002172 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002173 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002174 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002175 verts[2].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002176
Brian Salomon57caa662017-10-18 12:21:05 +00002177 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002178 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002179 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002180 verts[3].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
2181
2182 if (DIEllipseStyle::kStroke == this->style()) {
2183 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
2184 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
2185
2186 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2187 -innerRatioY - offsetDy);
2188 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2189 innerRatioY + offsetDy);
2190 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2191 -innerRatioY - offsetDy);
2192 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2193 innerRatioY + offsetDy);
2194 }
joshualitt76e7fb62015-02-11 08:52:27 -08002195
bsalomonb5238a72015-05-05 07:49:49 -07002196 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002197 }
Brian Salomon05441c42017-05-15 16:45:49 -04002198 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002199 }
halcanary9d524f22016-03-29 09:03:52 -07002200
Brian Salomon25a88092016-12-01 09:36:50 -05002201 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002202 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002203 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002204 return false;
2205 }
2206
bsalomoncdaa97b2016-03-08 08:30:14 -08002207 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08002208 return false;
2209 }
2210
joshualittd96a67b2015-05-05 14:09:05 -07002211 // TODO rewrite to allow positioning on CPU
2212 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08002213 return false;
2214 }
2215
Brian Salomon05441c42017-05-15 16:45:49 -04002216 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002217 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002218 return true;
2219 }
2220
Brian Salomon05441c42017-05-15 16:45:49 -04002221 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2222 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002223
Brian Salomon05441c42017-05-15 16:45:49 -04002224 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002225 SkMatrix fViewMatrix;
2226 GrColor fColor;
2227 SkScalar fXRadius;
2228 SkScalar fYRadius;
2229 SkScalar fInnerXRadius;
2230 SkScalar fInnerYRadius;
2231 SkScalar fGeoDx;
2232 SkScalar fGeoDy;
2233 DIEllipseStyle fStyle;
2234 SkRect fBounds;
2235 };
2236
Brian Salomon05441c42017-05-15 16:45:49 -04002237 Helper fHelper;
2238 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002239
Brian Salomon05441c42017-05-15 16:45:49 -04002240 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002241};
2242
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002243///////////////////////////////////////////////////////////////////////////////
2244
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002245// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002246//
2247// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2248// ____________
2249// |_|________|_|
2250// | | | |
2251// | | | |
2252// | | | |
2253// |_|________|_|
2254// |_|________|_|
2255//
2256// For strokes, we don't draw the center quad.
2257//
2258// For circular roundrects, in the case where the stroke width is greater than twice
2259// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002260// in the center. The shared vertices are duplicated so we can set a different outer radius
2261// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002262// ____________
2263// |_|________|_|
2264// | |\ ____ /| |
2265// | | | | | |
2266// | | |____| | |
2267// |_|/______\|_|
2268// |_|________|_|
2269//
2270// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002271//
2272// For filled rrects that need to provide a distance vector we resuse the overstroke
2273// geometry but make the inner rect degenerate (either a point or a horizontal or
2274// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002275
jvanverth84839f62016-08-29 10:16:40 -07002276static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002277 // clang-format off
2278 // overstroke quads
2279 // we place this at the beginning so that we can skip these indices when rendering normally
2280 16, 17, 19, 16, 19, 18,
2281 19, 17, 23, 19, 23, 21,
2282 21, 23, 22, 21, 22, 20,
2283 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002284
Brian Salomon289e3d82016-12-14 15:52:56 -05002285 // corners
2286 0, 1, 5, 0, 5, 4,
2287 2, 3, 7, 2, 7, 6,
2288 8, 9, 13, 8, 13, 12,
2289 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002290
Brian Salomon289e3d82016-12-14 15:52:56 -05002291 // edges
2292 1, 2, 6, 1, 6, 5,
2293 4, 5, 9, 4, 9, 8,
2294 6, 7, 11, 6, 11, 10,
2295 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002296
Brian Salomon289e3d82016-12-14 15:52:56 -05002297 // center
2298 // we place this at the end so that we can ignore these indices when not rendering as filled
2299 5, 6, 10, 5, 10, 9,
2300 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002301};
Brian Salomon289e3d82016-12-14 15:52:56 -05002302
jvanverth84839f62016-08-29 10:16:40 -07002303// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002304static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305
jvanverth84839f62016-08-29 10:16:40 -07002306// overstroke count is arraysize minus the center indices
2307static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2308// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002309static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002310// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002311static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2312static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002313static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002314
jvanverthc3d0e422016-08-25 08:12:35 -07002315enum RRectType {
2316 kFill_RRectType,
2317 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002318 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002319};
2320
jvanverth84839f62016-08-29 10:16:40 -07002321static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002322 switch (type) {
2323 case kFill_RRectType:
2324 case kStroke_RRectType:
2325 return kVertsPerStandardRRect;
2326 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002327 return kVertsPerOverstrokeRRect;
2328 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002329 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002330 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002331}
2332
2333static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002334 switch (type) {
2335 case kFill_RRectType:
2336 return kIndicesPerFillRRect;
2337 case kStroke_RRectType:
2338 return kIndicesPerStrokeRRect;
2339 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002340 return kIndicesPerOverstrokeRRect;
2341 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002342 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002343 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002344}
2345
2346static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002347 switch (type) {
2348 case kFill_RRectType:
2349 case kStroke_RRectType:
2350 return gStandardRRectIndices;
2351 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002352 return gOverstrokeRRectIndices;
2353 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002354 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002355 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002356}
2357
joshualitt76e7fb62015-02-11 08:52:27 -08002358///////////////////////////////////////////////////////////////////////////////////////////////////
2359
Robert Phillips79839d42016-10-06 15:03:34 -04002360// For distance computations in the interior of filled rrects we:
2361//
2362// add a interior degenerate (point or line) rect
2363// each vertex of that rect gets -outerRad as its radius
2364// this makes the computation of the distance to the outer edge be negative
2365// negative values are caught and then handled differently in the GP's onEmitCode
2366// each vertex is also given the normalized x & y distance from the interior rect's edge
2367// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2368
Brian Salomon05441c42017-05-15 16:45:49 -04002369class CircularRRectOp : public GrMeshDrawOp {
2370private:
2371 using Helper = GrSimpleMeshDrawOpHelper;
2372
joshualitt76e7fb62015-02-11 08:52:27 -08002373public:
Brian Salomon25a88092016-12-01 09:36:50 -05002374 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002375
bsalomon4b4a7cc2016-07-08 04:42:54 -07002376 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2377 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002378 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomone23bffd2017-06-02 11:01:10 -04002379 const SkRect& devRect, float devRadius,
2380 float devStrokeWidth, bool strokeOnly) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00002381 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomone23bffd2017-06-02 11:01:10 -04002382 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002383 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002384 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
2385 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002386 : INHERITED(ClassID())
2387 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002388 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002389 SkRect bounds = devRect;
2390 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2391 SkScalar innerRadius = 0.0f;
2392 SkScalar outerRadius = devRadius;
2393 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002394 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002395 if (devStrokeWidth > 0) {
2396 if (SkScalarNearlyZero(devStrokeWidth)) {
2397 halfWidth = SK_ScalarHalf;
2398 } else {
2399 halfWidth = SkScalarHalf(devStrokeWidth);
2400 }
joshualitt76e7fb62015-02-11 08:52:27 -08002401
bsalomon4b4a7cc2016-07-08 04:42:54 -07002402 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002403 // Outset stroke by 1/4 pixel
2404 devStrokeWidth += 0.25f;
2405 // If stroke is greater than width or height, this is still a fill
2406 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002407 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002408 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002409 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002410 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002411 }
2412 outerRadius += halfWidth;
2413 bounds.outset(halfWidth, halfWidth);
2414 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002415
bsalomon4b4a7cc2016-07-08 04:42:54 -07002416 // The radii are outset for two reasons. First, it allows the shader to simply perform
2417 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2418 // Second, the outer radius is used to compute the verts of the bounding box that is
2419 // rendered and the outset ensures the box will cover all partially covered by the rrect
2420 // corners.
2421 outerRadius += SK_ScalarHalf;
2422 innerRadius -= SK_ScalarHalf;
2423
bsalomon88cf17d2016-07-08 06:40:56 -07002424 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2425
2426 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002427 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2428
Brian Salomon05441c42017-05-15 16:45:49 -04002429 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002430 fVertCount = rrect_type_to_vert_count(type);
2431 fIndexCount = rrect_type_to_index_count(type);
2432 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002433 }
2434
Brian Salomon289e3d82016-12-14 15:52:56 -05002435 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002436
Robert Phillipsf1748f52017-09-14 14:11:24 -04002437 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002438 fHelper.visitProxies(func);
2439 }
2440
jvanverthc3d0e422016-08-25 08:12:35 -07002441 SkString dumpInfo() const override {
2442 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002443 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002444 string.appendf(
2445 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2446 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04002447 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
2448 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
2449 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002450 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002451 string += fHelper.dumpInfo();
2452 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002453 return string;
2454 }
2455
Brian Osman9a725dd2017-09-20 09:53:22 -04002456 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2457 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002458 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002459 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2460 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002461 }
2462
2463 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2464
Brian Salomon92aee3d2016-12-21 09:20:25 -05002465private:
Robert Phillips79839d42016-10-06 15:03:34 -04002466 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05002467 SkPoint fPos;
2468 GrColor fColor;
2469 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04002470 SkScalar fOuterRadius;
2471 SkScalar fInnerRadius;
2472 // No half plane, we don't use it here.
2473 };
2474
Brian Salomon289e3d82016-12-14 15:52:56 -05002475 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
2476 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2477 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002478 SkASSERT(smInset < bigInset);
2479
2480 // TL
2481 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
2482 (*verts)->fColor = color;
2483 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2484 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002485 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002486 (*verts)++;
2487
2488 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05002489 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002490 (*verts)->fColor = color;
2491 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2492 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002493 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002494 (*verts)++;
2495
Brian Salomon289e3d82016-12-14 15:52:56 -05002496 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002497 (*verts)->fColor = color;
2498 (*verts)->fOffset = SkPoint::Make(0, 0);
2499 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002500 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002501 (*verts)++;
2502
Brian Salomon289e3d82016-12-14 15:52:56 -05002503 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002504 (*verts)->fColor = color;
2505 (*verts)->fOffset = SkPoint::Make(0, 0);
2506 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002507 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002508 (*verts)++;
2509
2510 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
2511 (*verts)->fColor = color;
2512 (*verts)->fOffset = SkPoint::Make(0, 0);
2513 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002514 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002515 (*verts)++;
2516
2517 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
2518 (*verts)->fColor = color;
2519 (*verts)->fOffset = SkPoint::Make(0, 0);
2520 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002521 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002522 (*verts)++;
2523
2524 // BL
2525 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
2526 (*verts)->fColor = color;
2527 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2528 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002529 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002530 (*verts)++;
2531
2532 // BR
2533 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
2534 (*verts)->fColor = color;
2535 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2536 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002537 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002538 (*verts)++;
2539 }
2540
Brian Salomon91326c32017-08-09 16:02:19 -04002541 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002542 // Invert the view matrix as a local matrix (if any other processors require coords).
2543 SkMatrix localMatrix;
2544 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002545 return;
2546 }
2547
2548 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002549 sk_sp<GrGeometryProcessor> gp(
Brian Salomon45c92202018-04-10 10:53:58 -04002550 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002551
joshualitt76e7fb62015-02-11 08:52:27 -08002552 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07002553 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08002554
jvanverth84839f62016-08-29 10:16:40 -07002555 const GrBuffer* vertexBuffer;
2556 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002557
Brian Salomon289e3d82016-12-14 15:52:56 -05002558 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
2559 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07002560 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002561 SkDebugf("Could not allocate vertices\n");
2562 return;
2563 }
2564
jvanverth84839f62016-08-29 10:16:40 -07002565 const GrBuffer* indexBuffer = nullptr;
2566 int firstIndex = 0;
2567 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2568 if (!indices) {
2569 SkDebugf("Could not allocate indices\n");
2570 return;
2571 }
2572
2573 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002574 for (const auto& rrect : fRRects) {
2575 GrColor color = rrect.fColor;
2576 SkScalar outerRadius = rrect.fOuterRadius;
2577 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002578
Brian Salomon289e3d82016-12-14 15:52:56 -05002579 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2580 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002581
Brian Salomon289e3d82016-12-14 15:52:56 -05002582 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002583 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002584 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002585 SkScalar innerRadius = rrect.fType != kFill_RRectType
2586 ? rrect.fInnerRadius / rrect.fOuterRadius
2587 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002588 for (int i = 0; i < 4; ++i) {
2589 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002590 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002591 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
2592 verts->fOuterRadius = outerRadius;
2593 verts->fInnerRadius = innerRadius;
2594 verts++;
2595
2596 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002597 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002598 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2599 verts->fOuterRadius = outerRadius;
2600 verts->fInnerRadius = innerRadius;
2601 verts++;
2602
2603 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002604 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002605 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2606 verts->fOuterRadius = outerRadius;
2607 verts->fInnerRadius = innerRadius;
2608 verts++;
2609
2610 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002611 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002612 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
2613 verts->fOuterRadius = outerRadius;
2614 verts->fInnerRadius = innerRadius;
2615 verts++;
2616 }
jvanverthc3d0e422016-08-25 08:12:35 -07002617 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002618 // Effectively this is an additional stroked rrect, with its
2619 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2620 // This will give us correct AA in the center and the correct
2621 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002622 //
jvanvertha4f1af82016-08-29 07:17:47 -07002623 // Also, the outer offset is a constant vector pointing to the right, which
2624 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002625 if (kOverstroke_RRectType == rrect.fType) {
2626 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002627
Brian Salomon05441c42017-05-15 16:45:49 -04002628 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002629 // this is the normalized distance from the outer rectangle of this
2630 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002631 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002632
Brian Salomon289e3d82016-12-14 15:52:56 -05002633 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002634 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002635 }
jvanverth6a397612016-08-26 08:15:33 -07002636
Brian Salomon05441c42017-05-15 16:45:49 -04002637 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2638 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002639 for (int i = 0; i < primIndexCount; ++i) {
2640 *indices++ = primIndices[i] + currStartVertex;
2641 }
2642
Brian Salomon05441c42017-05-15 16:45:49 -04002643 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002644 }
2645
Chris Dalton3809bab2017-06-13 10:55:06 -06002646 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06002647 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
2648 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04002649 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002650 }
2651
Brian Salomon25a88092016-12-01 09:36:50 -05002652 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002653 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002654
2655 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002656 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002657 return false;
2658 }
2659
Brian Salomon05441c42017-05-15 16:45:49 -04002660 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002661 return false;
2662 }
2663
Brian Salomon05441c42017-05-15 16:45:49 -04002664 if (fHelper.usesLocalCoords() &&
2665 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002666 return false;
2667 }
2668
Brian Salomon05441c42017-05-15 16:45:49 -04002669 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002670 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002671 fVertCount += that->fVertCount;
2672 fIndexCount += that->fIndexCount;
2673 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002674 return true;
2675 }
2676
Brian Salomon05441c42017-05-15 16:45:49 -04002677 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002678 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002679 SkScalar fInnerRadius;
2680 SkScalar fOuterRadius;
2681 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002682 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002683 };
2684
Brian Salomon289e3d82016-12-14 15:52:56 -05002685 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002686 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002687 int fVertCount;
2688 int fIndexCount;
2689 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002690 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002691
Brian Salomon05441c42017-05-15 16:45:49 -04002692 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002693};
2694
jvanverth84839f62016-08-29 10:16:40 -07002695static const int kNumRRectsInIndexBuffer = 256;
2696
2697GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2698GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002699static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2700 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002701 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2702 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2703 switch (type) {
2704 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002705 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002706 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2707 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002708 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002709 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002710 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2711 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002712 default:
2713 SkASSERT(false);
2714 return nullptr;
2715 };
2716}
2717
Brian Salomon05441c42017-05-15 16:45:49 -04002718class EllipticalRRectOp : public GrMeshDrawOp {
2719private:
2720 using Helper = GrSimpleMeshDrawOpHelper;
2721
joshualitt76e7fb62015-02-11 08:52:27 -08002722public:
Brian Salomon25a88092016-12-01 09:36:50 -05002723 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002724
bsalomon4b4a7cc2016-07-08 04:42:54 -07002725 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2726 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002727 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002728 const SkRect& devRect, float devXRadius, float devYRadius,
2729 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002730 SkASSERT(devXRadius > 0.5);
2731 SkASSERT(devYRadius > 0.5);
2732 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2733 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002734 if (devStrokeWidths.fX > 0) {
2735 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2736 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2737 } else {
2738 devStrokeWidths.scale(SK_ScalarHalf);
2739 }
joshualitt76e7fb62015-02-11 08:52:27 -08002740
bsalomon4b4a7cc2016-07-08 04:42:54 -07002741 // we only handle thick strokes for near-circular ellipses
2742 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002743 (SK_ScalarHalf * devXRadius > devYRadius ||
2744 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002745 return nullptr;
2746 }
2747
2748 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002749 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2750 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002751 return nullptr;
2752 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002753 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2754 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002755 return nullptr;
2756 }
Brian Salomon05441c42017-05-15 16:45:49 -04002757 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002758 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002759 devXRadius, devYRadius, devStrokeWidths,
2760 strokeOnly);
2761 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002762
Brian Salomonea26d6b2018-01-23 20:33:21 +00002763 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2764 const SkRect& devRect, float devXRadius, float devYRadius,
2765 SkVector devStrokeHalfWidths, bool strokeOnly)
2766 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002767 SkScalar innerXRadius = 0.0f;
2768 SkScalar innerYRadius = 0.0f;
2769 SkRect bounds = devRect;
2770 bool stroked = false;
2771 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002772 // this is legit only if scale & translation (which should be the case at the moment)
2773 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002774 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2775 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002776 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2777 }
2778
Brian Salomon05441c42017-05-15 16:45:49 -04002779 devXRadius += devStrokeHalfWidths.fX;
2780 devYRadius += devStrokeHalfWidths.fY;
2781 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002782 }
2783
Brian Salomon05441c42017-05-15 16:45:49 -04002784 fStroked = stroked;
2785 fViewMatrixIfUsingLocalCoords = viewMatrix;
2786 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002787 // Expand the rect for aa in order to generate the correct vertices.
2788 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002789 fRRects.emplace_back(
2790 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002791 }
2792
Brian Salomon289e3d82016-12-14 15:52:56 -05002793 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002794
Robert Phillipsf1748f52017-09-14 14:11:24 -04002795 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002796 fHelper.visitProxies(func);
2797 }
2798
Brian Salomon7c3e7182016-12-01 09:35:30 -05002799 SkString dumpInfo() const override {
2800 SkString string;
2801 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002802 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002803 string.appendf(
2804 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2805 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2806 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2807 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2808 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002809 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002810 string += fHelper.dumpInfo();
2811 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002812 return string;
2813 }
2814
Brian Osman9a725dd2017-09-20 09:53:22 -04002815 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2816 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002817 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002818 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2819 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002820 }
2821
2822 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2823
bsalomone46f9fe2015-08-18 06:05:14 -07002824private:
Brian Salomon91326c32017-08-09 16:02:19 -04002825 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002826 SkMatrix localMatrix;
2827 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002828 return;
2829 }
2830
2831 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00002832 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002833
joshualitt76e7fb62015-02-11 08:52:27 -08002834 size_t vertexStride = gp->getVertexStride();
2835 SkASSERT(vertexStride == sizeof(EllipseVertex));
2836
bsalomonb5238a72015-05-05 07:49:49 -07002837 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002838 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002839 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2840 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002841
Chris Dalton3809bab2017-06-13 10:55:06 -06002842 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002843 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002844 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002845 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002846 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002847 SkDebugf("Could not allocate vertices\n");
2848 return;
2849 }
2850
Brian Salomon05441c42017-05-15 16:45:49 -04002851 for (const auto& rrect : fRRects) {
2852 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002853 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002854 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2855 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2856 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2857 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002858
2859 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002860 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2861 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002862
Brian Salomon05441c42017-05-15 16:45:49 -04002863 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002864
Brian Salomon289e3d82016-12-14 15:52:56 -05002865 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2866 bounds.fBottom - yOuterRadius, bounds.fBottom};
2867 SkScalar yOuterOffsets[4] = {yOuterRadius,
2868 SK_ScalarNearlyZero, // we're using inversesqrt() in
2869 // shader, so can't be exactly 0
2870 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002871
2872 for (int i = 0; i < 4; ++i) {
2873 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002874 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002875 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2876 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2877 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2878 verts++;
2879
2880 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002881 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002882 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2883 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2884 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2885 verts++;
2886
2887 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002888 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002889 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2890 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2891 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2892 verts++;
2893
2894 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002895 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002896 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2897 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2898 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2899 verts++;
2900 }
2901 }
Brian Salomon05441c42017-05-15 16:45:49 -04002902 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002903 }
2904
Brian Salomon25a88092016-12-01 09:36:50 -05002905 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002906 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002907
Brian Salomon05441c42017-05-15 16:45:49 -04002908 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002909 return false;
2910 }
2911
bsalomoncdaa97b2016-03-08 08:30:14 -08002912 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002913 return false;
2914 }
2915
Brian Salomon05441c42017-05-15 16:45:49 -04002916 if (fHelper.usesLocalCoords() &&
2917 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002918 return false;
2919 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002920
Brian Salomon05441c42017-05-15 16:45:49 -04002921 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002922 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002923 return true;
2924 }
2925
Brian Salomon05441c42017-05-15 16:45:49 -04002926 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002927 GrColor fColor;
2928 SkScalar fXRadius;
2929 SkScalar fYRadius;
2930 SkScalar fInnerXRadius;
2931 SkScalar fInnerYRadius;
2932 SkRect fDevBounds;
2933 };
2934
Brian Salomon289e3d82016-12-14 15:52:56 -05002935 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002936 Helper fHelper;
2937 bool fStroked;
2938 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002939
Brian Salomon05441c42017-05-15 16:45:49 -04002940 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002941};
2942
Brian Salomon05441c42017-05-15 16:45:49 -04002943static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002944 const SkMatrix& viewMatrix,
2945 const SkRRect& rrect,
2946 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002947 SkASSERT(viewMatrix.rectStaysRect());
2948 SkASSERT(rrect.isSimple());
2949 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002950
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002951 // RRect ops only handle simple, but not too simple, rrects.
2952 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002953 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002954 SkRect bounds;
2955 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002956
Mike Reed242135a2018-02-22 13:41:39 -05002957 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05002958 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2959 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2960 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2961 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002962
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002963 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002964
bsalomon4b4a7cc2016-07-08 04:42:54 -07002965 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2966 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002967 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002968
Brian Salomon289e3d82016-12-14 15:52:56 -05002969 bool isStrokeOnly =
2970 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002971 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2972
jvanverthc3d0e422016-08-25 08:12:35 -07002973 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002974 if (hasStroke) {
2975 if (SkStrokeRec::kHairline_Style == style) {
2976 scaledStroke.set(1, 1);
2977 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002978 scaledStroke.fX = SkScalarAbs(
2979 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2980 scaledStroke.fY = SkScalarAbs(
2981 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002982 }
2983
jvanverthc3d0e422016-08-25 08:12:35 -07002984 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2985 // for non-circular rrects, if half of strokewidth is greater than radius,
2986 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002987 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2988 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002989 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002990 }
2991 }
2992
2993 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2994 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2995 // patch will have fractional coverage. This only matters when the interior is actually filled.
2996 // We could consider falling back to rect rendering here, since a tiny radius is
2997 // indistinguishable from a square corner.
2998 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002999 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003000 }
3001
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003002 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003003 if (isCircular) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003004 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
3005 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003006 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003007 } else {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003008 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
Brian Salomon05441c42017-05-15 16:45:49 -04003009 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003010 }
joshualitt3e708c52015-04-30 13:49:27 -07003011}
3012
Brian Salomonea26d6b2018-01-23 20:33:21 +00003013std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
3014 const SkMatrix& viewMatrix,
3015 const SkRRect& rrect,
3016 const SkStrokeRec& stroke,
3017 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003018 if (rrect.isOval()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003019 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), GrStyle(stroke, nullptr),
3020 shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003021 }
3022
3023 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003024 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003025 }
3026
Brian Salomonea26d6b2018-01-23 20:33:21 +00003027 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003028}
joshualitt3e708c52015-04-30 13:49:27 -07003029
bsalomon4b4a7cc2016-07-08 04:42:54 -07003030///////////////////////////////////////////////////////////////////////////////
3031
Brian Salomonea26d6b2018-01-23 20:33:21 +00003032std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
3033 const SkMatrix& viewMatrix,
3034 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003035 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003036 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003037 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003038 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003039 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3040 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003041 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003042 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003043 if (style.hasNonDashPathEffect()) {
3044 return nullptr;
3045 } else if (style.isDashed()) {
3046 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3047 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3048 return nullptr;
3049 }
3050 auto onInterval = style.dashIntervals()[0];
3051 auto offInterval = style.dashIntervals()[1];
3052 if (offInterval == 0) {
3053 GrStyle strokeStyle(style.strokeRec(), nullptr);
3054 return MakeOvalOp(std::move(paint), viewMatrix, oval, strokeStyle, shaderCaps);
3055 } else if (onInterval == 0) {
3056 // There is nothing to draw but we have no way to indicate that here.
3057 return nullptr;
3058 }
3059 auto angularOnInterval = onInterval / r;
3060 auto angularOffInterval = offInterval / r;
3061 auto phaseAngle = style.dashPhase() / r;
3062 // Currently this function doesn't accept ovals with different start angles, though
3063 // it could.
3064 static const SkScalar kStartAngle = 0.f;
3065 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, r,
3066 style.strokeRec().getWidth(), kStartAngle,
3067 angularOnInterval, angularOffInterval, phaseAngle);
3068 }
3069 return CircleOp::Make(std::move(paint), viewMatrix, center, r, style);
3070 }
3071
3072 if (style.pathEffect()) {
3073 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003074 }
3075
Stan Ilieveb868aa2017-02-21 11:06:16 -05003076 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003077 if (viewMatrix.rectStaysRect()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003078 return EllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003079 }
3080
Stan Ilieveb868aa2017-02-21 11:06:16 -05003081 // Otherwise, if we have shader derivative support, render as device-independent
3082 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003083 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
Stan Ilieveb868aa2017-02-21 11:06:16 -05003084 }
3085
bsalomon4b4a7cc2016-07-08 04:42:54 -07003086 return nullptr;
3087}
3088
3089///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003090
Brian Salomonea26d6b2018-01-23 20:33:21 +00003091std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
3092 const SkRect& oval, SkScalar startAngle,
3093 SkScalar sweepAngle, bool useCenter,
3094 const GrStyle& style,
3095 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003096 SkASSERT(!oval.isEmpty());
3097 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003098 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003099 if (SkScalarAbs(sweepAngle) >= 360.f) {
3100 return nullptr;
3101 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003102 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3103 return nullptr;
3104 }
3105 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003106 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3107 useCenter};
Brian Salomonea26d6b2018-01-23 20:33:21 +00003108 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003109}
3110
3111///////////////////////////////////////////////////////////////////////////////
3112
Hal Canary6f6961e2017-01-31 13:50:44 -05003113#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003114
Brian Salomon05441c42017-05-15 16:45:49 -04003115GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003116 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003117 SkScalar rotate = random->nextSScalar1() * 360.f;
3118 SkScalar translateX = random->nextSScalar1() * 1000.f;
3119 SkScalar translateY = random->nextSScalar1() * 1000.f;
3120 SkScalar scale = random->nextSScalar1() * 100.f;
3121 SkMatrix viewMatrix;
3122 viewMatrix.setRotate(rotate);
3123 viewMatrix.postTranslate(translateX, translateY);
3124 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003125 SkRect circle = GrTest::TestSquare(random);
3126 SkPoint center = {circle.centerX(), circle.centerY()};
3127 SkScalar radius = circle.width() / 2.f;
3128 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003129 CircleOp::ArcParams arcParamsTmp;
3130 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003131 if (random->nextBool()) {
3132 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003133 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3134 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003135 arcParams = &arcParamsTmp;
3136 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003137 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
3138 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003139 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003140 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003141 }
3142 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003143}
3144
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003145GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3146 SkScalar rotate = random->nextSScalar1() * 360.f;
3147 SkScalar translateX = random->nextSScalar1() * 1000.f;
3148 SkScalar translateY = random->nextSScalar1() * 1000.f;
3149 SkScalar scale = random->nextSScalar1() * 100.f;
3150 SkMatrix viewMatrix;
3151 viewMatrix.setRotate(rotate);
3152 viewMatrix.postTranslate(translateX, translateY);
3153 viewMatrix.postScale(scale, scale);
3154 SkRect circle = GrTest::TestSquare(random);
3155 SkPoint center = {circle.centerX(), circle.centerY()};
3156 SkScalar radius = circle.width() / 2.f;
3157 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3158 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3159 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3160 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3161 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
3162 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, radius, strokeWidth,
3163 startAngle, onAngle, offAngle, phase);
3164}
3165
Brian Salomon05441c42017-05-15 16:45:49 -04003166GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003167 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003168 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003169 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003170}
3171
Brian Salomon05441c42017-05-15 16:45:49 -04003172GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003173 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003174 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003175 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003176}
3177
Brian Salomon05441c42017-05-15 16:45:49 -04003178GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003179 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003180 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003181 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003182}
3183
3184#endif