blob: 5761aeff265fa68115e7855e00d6d5fc6a80f276 [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(),
Brian Salomon70132d02018-05-29 15:33:06 -0400155 cgp.fInCircleEdge->name());
Brian Salomon45c92202018-04-10 10:53:58 -0400156 }
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 Salomon70132d02018-05-29 15:33:06 -0400162 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->name());
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);
Brian Salomon70132d02018-05-29 15:33:06 -0400317 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams->name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400318 // 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
Brian Salomon70132d02018-05-29 15:33:06 -0400383 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition->name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400384
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(),
Brian Salomon70132d02018-05-29 15:33:06 -0400552 egp.fInEllipseOffset->name());
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 Salomon70132d02018-05-29 15:33:06 -0400556 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->name());
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 Salomon70132d02018-05-29 15:33:06 -0400563 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->name());
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 Salomon70132d02018-05-29 15:33:06 -0400691 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
692 diegp.fInEllipseOffsets0->name());
joshualitt74077b92014-10-24 11:26:03 -0700693
Chris Dalton27372882017-12-08 13:34:21 -0700694 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800695 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon70132d02018-05-29 15:33:06 -0400696 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
697 diegp.fInEllipseOffsets1->name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800698
Chris Dalton60283612018-02-14 13:38:14 -0700699 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700700 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800701
joshualittabb52a12015-01-13 15:02:10 -0800702 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400703 this->writeOutputPosition(vertBuilder,
704 uniformHandler,
705 gpArgs,
Brian Salomon70132d02018-05-29 15:33:06 -0400706 diegp.fInPosition->name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400707 diegp.fViewMatrix,
708 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800709
710 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800711 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800712 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800713 uniformHandler,
Brian Salomon04460cc2017-12-06 14:47:42 -0500714 diegp.fInPosition->asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700715 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800716
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000717 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400718 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
719 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
720 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
721 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500722 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400723 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
724 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500725 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000726
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400727 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000728 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800729 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400730 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800731 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000732 // can probably do this with one step
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400733 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800734 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000735 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400736 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000737 }
738
739 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800740 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800741 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
742 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
743 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
744 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500745 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400746 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
747 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500748 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800749 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
750 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000751 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000752
753 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000754 }
755
robertphillips46d36f02015-01-18 08:14:14 -0800756 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500757 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700758 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800759 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
760 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700761 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700762 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000763 }
764
bsalomona624bf32016-09-20 09:12:47 -0700765 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
766 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800767 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700768
bsalomon31df31c2016-08-17 09:00:24 -0700769 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
770 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700771 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800772 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700773 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
774 }
bsalomona624bf32016-09-20 09:12:47 -0700775 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000776 }
777
778 private:
joshualitt5559ca22015-05-21 15:50:36 -0700779 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700780 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800781
egdaniele659a582015-11-13 09:55:43 -0800782 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000783 };
784
joshualitt71c92602015-01-14 08:12:47 -0800785 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800786 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800787 const Attribute* fInEllipseOffsets0;
788 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500789 SkMatrix fViewMatrix;
790 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000791
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400792 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000793
joshualitt249af152014-09-15 11:41:13 -0700794 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000795};
796
bsalomoncdaa97b2016-03-08 08:30:14 -0800797GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000798
Hal Canary6f6961e2017-01-31 13:50:44 -0500799#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700800sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Salomonea26d6b2018-01-23 20:33:21 +0000802 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000803}
Hal Canary6f6961e2017-01-31 13:50:44 -0500804#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000805
806///////////////////////////////////////////////////////////////////////////////
807
jvanverth6ca48822016-10-07 06:57:32 -0700808// We have two possible cases for geometry for a circle:
809
810// In the case of a normal fill, we draw geometry for the circle as an octagon.
811static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500812 // enter the octagon
813 // clang-format off
814 0, 1, 8, 1, 2, 8,
815 2, 3, 8, 3, 4, 8,
816 4, 5, 8, 5, 6, 8,
817 6, 7, 8, 7, 0, 8
818 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700819};
820
821// For stroked circles, we use two nested octagons.
822static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500823 // enter the octagon
824 // clang-format off
825 0, 1, 9, 0, 9, 8,
826 1, 2, 10, 1, 10, 9,
827 2, 3, 11, 2, 11, 10,
828 3, 4, 12, 3, 12, 11,
829 4, 5, 13, 4, 13, 12,
830 5, 6, 14, 5, 14, 13,
831 6, 7, 15, 6, 15, 14,
832 7, 0, 8, 7, 8, 15,
833 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700834};
835
Brian Salomon289e3d82016-12-14 15:52:56 -0500836
jvanverth6ca48822016-10-07 06:57:32 -0700837static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
838static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
839static const int kVertsPerStrokeCircle = 16;
840static const int kVertsPerFillCircle = 9;
841
842static int circle_type_to_vert_count(bool stroked) {
843 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
844}
845
846static int circle_type_to_index_count(bool stroked) {
847 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
848}
849
850static const uint16_t* circle_type_to_indices(bool stroked) {
851 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
852}
853
854///////////////////////////////////////////////////////////////////////////////
855
Brian Salomon05441c42017-05-15 16:45:49 -0400856class CircleOp final : public GrMeshDrawOp {
857private:
858 using Helper = GrSimpleMeshDrawOpHelper;
859
joshualitt76e7fb62015-02-11 08:52:27 -0800860public:
Brian Salomon25a88092016-12-01 09:36:50 -0500861 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700862
bsalomon4f3a0ca2016-08-22 13:14:26 -0700863 /** Optional extra params to render a partial arc rather than a full circle. */
864 struct ArcParams {
865 SkScalar fStartAngleRadians;
866 SkScalar fSweepAngleRadians;
867 bool fUseCenter;
868 };
Brian Salomon05441c42017-05-15 16:45:49 -0400869
Brian Salomonea26d6b2018-01-23 20:33:21 +0000870 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400871 SkPoint center, SkScalar radius, const GrStyle& style,
872 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700873 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700874 if (style.hasPathEffect()) {
875 return nullptr;
876 }
Brian Salomon05441c42017-05-15 16:45:49 -0400877 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700878 SkStrokeRec::Style recStyle = stroke.getStyle();
879 if (arcParams) {
880 // Arc support depends on the style.
881 switch (recStyle) {
882 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500883 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700884 return nullptr;
885 case SkStrokeRec::kFill_Style:
886 // This supports all fills.
887 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400888 case SkStrokeRec::kStroke_Style:
889 // Strokes that don't use the center point are supported with butt and round
890 // caps.
891 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
892 return nullptr;
893 }
894 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700895 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400896 // Hairline only supports butt cap. Round caps could be emulated by slightly
897 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
899 return nullptr;
900 }
901 break;
902 }
903 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000904 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
905 arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400906 }
907
Brian Salomonea26d6b2018-01-23 20:33:21 +0000908 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400909 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000910 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400911 const SkStrokeRec& stroke = style.strokeRec();
912 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700913
Brian Salomon45c92202018-04-10 10:53:58 -0400914 fRoundCaps = false;
915
bsalomon4b4a7cc2016-07-08 04:42:54 -0700916 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700917 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700918 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800919
Brian Salomon289e3d82016-12-14 15:52:56 -0500920 bool isStrokeOnly =
921 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700922 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700923
jvanverth6ca48822016-10-07 06:57:32 -0700924 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700925 SkScalar outerRadius = radius;
926 SkScalar halfWidth = 0;
927 if (hasStroke) {
928 if (SkScalarNearlyZero(strokeWidth)) {
929 halfWidth = SK_ScalarHalf;
930 } else {
931 halfWidth = SkScalarHalf(strokeWidth);
932 }
933
934 outerRadius += halfWidth;
935 if (isStrokeOnly) {
936 innerRadius = radius - halfWidth;
937 }
938 }
939
940 // The radii are outset for two reasons. First, it allows the shader to simply perform
941 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
942 // Second, the outer radius is used to compute the verts of the bounding box that is
943 // rendered and the outset ensures the box will cover all partially covered by the circle.
944 outerRadius += SK_ScalarHalf;
945 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700946 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400947 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700948
bsalomon4f3a0ca2016-08-22 13:14:26 -0700949 // This makes every point fully inside the intersection plane.
950 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
951 // This makes every point fully outside the union plane.
952 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400953 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700954 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
955 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700956 if (arcParams) {
957 // The shader operates in a space where the circle is translated to be centered at the
958 // origin. Here we compute points on the unit circle at the starting and ending angles.
959 SkPoint startPoint, stopPoint;
960 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
961 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
962 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400963
964 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
965 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
966 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
967 startPoint.normalize();
968 stopPoint.normalize();
969
970 // If the matrix included scale (on one axis) we need to swap our start and end points
971 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
972 SkTSwap(startPoint, stopPoint);
973 }
974
Brian Salomon45c92202018-04-10 10:53:58 -0400975 fRoundCaps = style.strokeRec().getWidth() > 0 &&
976 style.strokeRec().getCap() == SkPaint::kRound_Cap;
977 SkPoint roundCaps[2];
978 if (fRoundCaps) {
979 // Compute the cap center points in the normalized space.
980 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
981 roundCaps[0] = startPoint * midRadius;
982 roundCaps[1] = stopPoint * midRadius;
983 } else {
984 roundCaps[0] = kUnusedRoundCaps[0];
985 roundCaps[1] = kUnusedRoundCaps[1];
986 }
987
bsalomon4f3a0ca2016-08-22 13:14:26 -0700988 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -0400989 // radial lines. We treat round caps the same way, but tack coverage of circles at the
990 // center of the butts.
991 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700992 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -0400993 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -0700994 // case.
Brian Salomon45c92202018-04-10 10:53:58 -0400995 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
996 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
997 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 if (useCenter) {
999 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1000 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
1001 if (arcParams->fSweepAngleRadians > 0) {
1002 norm0.negate();
1003 } else {
1004 norm1.negate();
1005 }
Brian Salomon05441c42017-05-15 16:45:49 -04001006 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001007 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001008 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001009 color,
1010 innerRadius,
1011 outerRadius,
1012 {norm0.fX, norm0.fY, 0.5f},
1013 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1014 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001015 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001016 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001017 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001018 fClipPlaneIsect = false;
1019 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001020 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001021 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001022 color,
1023 innerRadius,
1024 outerRadius,
1025 {norm0.fX, norm0.fY, 0.5f},
1026 {norm1.fX, norm1.fY, 0.5f},
1027 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001028 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001029 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001030 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001031 fClipPlaneIsect = true;
1032 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001033 }
1034 } else {
1035 // We clip to a secant of the original circle.
1036 startPoint.scale(radius);
1037 stopPoint.scale(radius);
1038 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1039 norm.normalize();
1040 if (arcParams->fSweepAngleRadians > 0) {
1041 norm.negate();
1042 }
1043 SkScalar d = -norm.dot(startPoint) + 0.5f;
1044
Brian Salomon05441c42017-05-15 16:45:49 -04001045 fCircles.emplace_back(
1046 Circle{color,
1047 innerRadius,
1048 outerRadius,
1049 {norm.fX, norm.fY, d},
1050 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1051 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001052 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001053 devBounds,
1054 stroked});
1055 fClipPlane = true;
1056 fClipPlaneIsect = false;
1057 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001058 }
1059 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001060 fCircles.emplace_back(
1061 Circle{color,
1062 innerRadius,
1063 outerRadius,
1064 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1065 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1066 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001067 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001068 devBounds,
1069 stroked});
1070 fClipPlane = false;
1071 fClipPlaneIsect = false;
1072 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001073 }
bsalomon88cf17d2016-07-08 06:40:56 -07001074 // Use the original radius and stroke radius for the bounds so that it does not include the
1075 // AA bloat.
1076 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001077 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001078 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1079 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001080 fVertCount = circle_type_to_vert_count(stroked);
1081 fIndexCount = circle_type_to_index_count(stroked);
1082 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001083 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001084
Brian Salomon289e3d82016-12-14 15:52:56 -05001085 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001086
Robert Phillipsf1748f52017-09-14 14:11:24 -04001087 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001088 fHelper.visitProxies(func);
1089 }
1090
robertphillipse004bfc2015-11-16 09:06:59 -08001091 SkString dumpInfo() const override {
1092 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001093 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001094 string.appendf(
1095 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1096 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001097 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1098 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1099 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001100 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001101 string += fHelper.dumpInfo();
1102 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001103 return string;
1104 }
1105
Brian Osman9a725dd2017-09-20 09:53:22 -04001106 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1107 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001108 GrColor* color = &fCircles.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001109 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1110 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001111 }
1112
1113 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1114
bsalomone46f9fe2015-08-18 06:05:14 -07001115private:
Brian Salomon91326c32017-08-09 16:02:19 -04001116 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001117 SkMatrix localMatrix;
1118 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001119 return;
1120 }
1121
1122 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001123 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Salomon45c92202018-04-10 10:53:58 -04001124 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125
1126 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001127 SkPoint fPos;
1128 GrColor fColor;
1129 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001130 SkScalar fOuterRadius;
1131 SkScalar fInnerRadius;
1132 // These planes may or may not be present in the vertex buffer.
1133 SkScalar fHalfPlanes[3][3];
1134 };
joshualitt76e7fb62015-02-11 08:52:27 -08001135
Brian Salomon45c92202018-04-10 10:53:58 -04001136 int numPlanes = (int)fClipPlane + fClipPlaneIsect + fClipPlaneUnion;
1137 auto vertexCapCenters = [numPlanes](CircleVertex* v) {
1138 return (void*)(v->fHalfPlanes + numPlanes);
1139 };
joshualitt76e7fb62015-02-11 08:52:27 -08001140 size_t vertexStride = gp->getVertexStride();
Brian Salomon45c92202018-04-10 10:53:58 -04001141 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
1142 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
1143 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
1144 (fRoundCaps ? 2 * sizeof(SkPoint) : 0));
jvanverth6ca48822016-10-07 06:57:32 -07001145
1146 const GrBuffer* vertexBuffer;
1147 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -05001148 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1149 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001150 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -07001151 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001152 return;
1153 }
1154
jvanverth6ca48822016-10-07 06:57:32 -07001155 const GrBuffer* indexBuffer = nullptr;
1156 int firstIndex = 0;
1157 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1158 if (!indices) {
1159 SkDebugf("Could not allocate indices\n");
1160 return;
1161 }
1162
1163 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001164 for (const auto& circle : fCircles) {
1165 SkScalar innerRadius = circle.fInnerRadius;
1166 SkScalar outerRadius = circle.fOuterRadius;
1167 GrColor color = circle.fColor;
1168 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001169
Brian Salomon289e3d82016-12-14 15:52:56 -05001170 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
1171 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
1172 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
1173 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
1174 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
1175 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
1176 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
1177 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001178
1179 // The inner radius in the vertex data must be specified in normalized space.
1180 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001181
1182 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001183 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -07001184 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -05001185
Brian Salomon289e3d82016-12-14 15:52:56 -05001186 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001187 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001188 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001189 v0->fOuterRadius = outerRadius;
1190 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001191
Brian Salomon289e3d82016-12-14 15:52:56 -05001192 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001193 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001194 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 v1->fOuterRadius = outerRadius;
1196 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001197
Brian Salomon289e3d82016-12-14 15:52:56 -05001198 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001199 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001200 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001201 v2->fOuterRadius = outerRadius;
1202 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001203
Brian Salomon289e3d82016-12-14 15:52:56 -05001204 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001205 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001206 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001207 v3->fOuterRadius = outerRadius;
1208 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001209
Brian Salomon289e3d82016-12-14 15:52:56 -05001210 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001211 v4->fColor = color;
1212 v4->fOffset = SkPoint::Make(octOffset, 1);
1213 v4->fOuterRadius = outerRadius;
1214 v4->fInnerRadius = innerRadius;
1215
Brian Salomon289e3d82016-12-14 15:52:56 -05001216 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001217 v5->fColor = color;
1218 v5->fOffset = SkPoint::Make(-octOffset, 1);
1219 v5->fOuterRadius = outerRadius;
1220 v5->fInnerRadius = innerRadius;
1221
Brian Salomon289e3d82016-12-14 15:52:56 -05001222 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001223 v6->fColor = color;
1224 v6->fOffset = SkPoint::Make(-1, octOffset);
1225 v6->fOuterRadius = outerRadius;
1226 v6->fInnerRadius = innerRadius;
1227
Brian Salomon289e3d82016-12-14 15:52:56 -05001228 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001229 v7->fColor = color;
1230 v7->fOffset = SkPoint::Make(-1, -octOffset);
1231 v7->fOuterRadius = outerRadius;
1232 v7->fInnerRadius = innerRadius;
1233
bsalomon4f3a0ca2016-08-22 13:14:26 -07001234 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001235 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1236 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1237 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1238 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1239 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1240 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1241 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1242 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001243 }
1244 int unionIdx = 1;
1245 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001246 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1247 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1248 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1249 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1250 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1251 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1252 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1253 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001254 unionIdx = 2;
1255 }
1256 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001257 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1258 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1259 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1260 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1261 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1262 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1263 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1264 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001265 }
Brian Salomon45c92202018-04-10 10:53:58 -04001266 if (fRoundCaps) {
1267 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1268 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1269 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1270 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1271 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1272 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1273 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1274 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1275 }
jvanverth6ca48822016-10-07 06:57:32 -07001276
Brian Salomon05441c42017-05-15 16:45:49 -04001277 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001278 // compute the inner ring
1279 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1280 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
1281 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
1282 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
1283 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
1284 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
1285 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
1286 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
1287
1288 // cosine and sine of pi/8
1289 SkScalar c = 0.923579533f;
1290 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -04001291 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001292
Brian Salomon289e3d82016-12-14 15:52:56 -05001293 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001294 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001295 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001296 v0->fOuterRadius = outerRadius;
1297 v0->fInnerRadius = innerRadius;
1298
Brian Salomon289e3d82016-12-14 15:52:56 -05001299 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001300 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001301 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001302 v1->fOuterRadius = outerRadius;
1303 v1->fInnerRadius = innerRadius;
1304
Brian Salomon289e3d82016-12-14 15:52:56 -05001305 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001306 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001307 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001308 v2->fOuterRadius = outerRadius;
1309 v2->fInnerRadius = innerRadius;
1310
Brian Salomon289e3d82016-12-14 15:52:56 -05001311 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001312 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001313 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001314 v3->fOuterRadius = outerRadius;
1315 v3->fInnerRadius = innerRadius;
1316
Brian Salomon289e3d82016-12-14 15:52:56 -05001317 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001318 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001319 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001320 v4->fOuterRadius = outerRadius;
1321 v4->fInnerRadius = innerRadius;
1322
Brian Salomon289e3d82016-12-14 15:52:56 -05001323 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001324 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001325 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001326 v5->fOuterRadius = outerRadius;
1327 v5->fInnerRadius = innerRadius;
1328
Brian Salomon289e3d82016-12-14 15:52:56 -05001329 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001330 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001331 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001332 v6->fOuterRadius = outerRadius;
1333 v6->fInnerRadius = innerRadius;
1334
Brian Salomon289e3d82016-12-14 15:52:56 -05001335 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001336 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001337 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001338 v7->fOuterRadius = outerRadius;
1339 v7->fInnerRadius = innerRadius;
1340
1341 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001342 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1343 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1344 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1345 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1346 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1347 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1348 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1349 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001350 }
1351 int unionIdx = 1;
1352 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001353 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1354 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1355 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1356 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1357 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1358 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1359 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1360 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001361 unionIdx = 2;
1362 }
1363 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001364 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1365 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1366 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1367 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1368 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1369 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1370 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1371 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001372 }
Brian Salomon45c92202018-04-10 10:53:58 -04001373 if (fRoundCaps) {
1374 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1375 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1376 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1377 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1378 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1379 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1380 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1381 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1382 }
jvanverth6ca48822016-10-07 06:57:32 -07001383 } else {
1384 // filled
1385 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1386 v8->fPos = center;
1387 v8->fColor = color;
1388 v8->fOffset = SkPoint::Make(0, 0);
1389 v8->fOuterRadius = outerRadius;
1390 v8->fInnerRadius = innerRadius;
1391 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001392 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001393 }
1394 int unionIdx = 1;
1395 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001396 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001397 unionIdx = 2;
1398 }
1399 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001400 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001401 }
Brian Salomon45c92202018-04-10 10:53:58 -04001402 SkASSERT(!fRoundCaps);
jvanverth6ca48822016-10-07 06:57:32 -07001403 }
1404
Brian Salomon05441c42017-05-15 16:45:49 -04001405 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1406 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001407 for (int i = 0; i < primIndexCount; ++i) {
1408 *indices++ = primIndices[i] + currStartVertex;
1409 }
1410
Brian Salomon05441c42017-05-15 16:45:49 -04001411 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1412 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001413 }
jvanverth6ca48822016-10-07 06:57:32 -07001414
Chris Dalton3809bab2017-06-13 10:55:06 -06001415 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001416 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1417 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001418 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001419 }
1420
Brian Salomon25a88092016-12-01 09:36:50 -05001421 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001422 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001423
1424 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001425 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001426 return false;
1427 }
1428
Brian Salomon05441c42017-05-15 16:45:49 -04001429 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001430 return false;
1431 }
1432
Brian Salomon05441c42017-05-15 16:45:49 -04001433 if (fHelper.usesLocalCoords() &&
1434 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001435 return false;
1436 }
1437
Brian Salomon289e3d82016-12-14 15:52:56 -05001438 // Because we've set up the ops that don't use the planes with noop values
1439 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001440 fClipPlane |= that->fClipPlane;
1441 fClipPlaneIsect |= that->fClipPlaneIsect;
1442 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001443 fRoundCaps |= that->fRoundCaps;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001444
Brian Salomon05441c42017-05-15 16:45:49 -04001445 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001446 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001447 fVertCount += that->fVertCount;
1448 fIndexCount += that->fIndexCount;
1449 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001450 return true;
1451 }
1452
Brian Salomon05441c42017-05-15 16:45:49 -04001453 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001454 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 SkScalar fInnerRadius;
1456 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001457 SkScalar fClipPlane[3];
1458 SkScalar fIsectPlane[3];
1459 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001460 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 SkRect fDevBounds;
1462 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001463 };
1464
Brian Salomon289e3d82016-12-14 15:52:56 -05001465 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001466 Helper fHelper;
1467 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001468 int fVertCount;
1469 int fIndexCount;
1470 bool fAllFill;
1471 bool fClipPlane;
1472 bool fClipPlaneIsect;
1473 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001474 bool fRoundCaps;
reed1b55a962015-09-17 20:16:13 -07001475
Brian Salomon05441c42017-05-15 16:45:49 -04001476 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001477};
1478
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001479class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1480private:
1481 using Helper = GrSimpleMeshDrawOpHelper;
1482
1483public:
1484 DEFINE_OP_CLASS_ID
1485
1486 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1487 SkPoint center, SkScalar radius, SkScalar strokeWidth,
1488 SkScalar startAngle, SkScalar onAngle, SkScalar offAngle,
1489 SkScalar phaseAngle) {
1490 SkASSERT(circle_stays_circle(viewMatrix));
1491 SkASSERT(strokeWidth < 2 * radius);
1492 return Helper::FactoryHelper<ButtCapDashedCircleOp>(std::move(paint), viewMatrix, center,
1493 radius, strokeWidth, startAngle,
1494 onAngle, offAngle, phaseAngle);
1495 }
1496
1497 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color,
1498 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1499 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1500 SkScalar offAngle, SkScalar phaseAngle)
1501 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1502 SkASSERT(circle_stays_circle(viewMatrix));
1503 viewMatrix.mapPoints(&center, 1);
1504 radius = viewMatrix.mapRadius(radius);
1505 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1506
1507 // Determine the angle where the circle starts in device space and whether its orientation
1508 // has been reversed.
1509 SkVector start;
1510 bool reflection;
1511 if (!startAngle) {
1512 start = {1, 0};
1513 } else {
1514 start.fY = SkScalarSinCos(startAngle, &start.fX);
1515 }
1516 viewMatrix.mapVectors(&start, 1);
1517 startAngle = SkScalarATan2(start.fY, start.fX);
1518 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1519 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1520
1521 auto totalAngle = onAngle + offAngle;
1522 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1523
1524 SkScalar halfWidth = 0;
1525 if (SkScalarNearlyZero(strokeWidth)) {
1526 halfWidth = SK_ScalarHalf;
1527 } else {
1528 halfWidth = SkScalarHalf(strokeWidth);
1529 }
1530
1531 SkScalar outerRadius = radius + halfWidth;
1532 SkScalar innerRadius = radius - halfWidth;
1533
1534 // The radii are outset for two reasons. First, it allows the shader to simply perform
1535 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1536 // Second, the outer radius is used to compute the verts of the bounding box that is
1537 // rendered and the outset ensures the box will cover all partially covered by the circle.
1538 outerRadius += SK_ScalarHalf;
1539 innerRadius -= SK_ScalarHalf;
1540 fViewMatrixIfUsingLocalCoords = viewMatrix;
1541
1542 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1543 center.fX + outerRadius, center.fY + outerRadius);
1544
1545 // We store whether there is a reflection as a negative total angle.
1546 if (reflection) {
1547 totalAngle = -totalAngle;
1548 }
1549 fCircles.push_back(Circle{
1550 color,
1551 outerRadius,
1552 innerRadius,
1553 onAngle,
1554 totalAngle,
1555 startAngle,
1556 phaseAngle,
1557 devBounds
1558 });
1559 // Use the original radius and stroke radius for the bounds so that it does not include the
1560 // AA bloat.
1561 radius += halfWidth;
1562 this->setBounds(
1563 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1564 HasAABloat::kYes, IsZeroArea::kNo);
1565 fVertCount = circle_type_to_vert_count(true);
1566 fIndexCount = circle_type_to_index_count(true);
1567 }
1568
1569 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1570
1571 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); }
1572
1573 SkString dumpInfo() const override {
1574 SkString string;
1575 for (int i = 0; i < fCircles.count(); ++i) {
1576 string.appendf(
1577 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1578 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1579 "Phase: %.2f\n",
1580 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1581 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1582 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle,
1583 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle);
1584 }
1585 string += fHelper.dumpInfo();
1586 string += INHERITED::dumpInfo();
1587 return string;
1588 }
1589
1590 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1591 GrPixelConfigIsClamped dstIsClamped) override {
1592 GrColor* color = &fCircles.front().fColor;
1593 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1594 GrProcessorAnalysisCoverage::kSingleChannel, color);
1595 }
1596
1597 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1598
1599private:
1600 void onPrepareDraws(Target* target) override {
1601 SkMatrix localMatrix;
1602 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1603 return;
1604 }
1605
1606 // Setup geometry processor
1607 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix));
1608
1609 struct CircleVertex {
1610 SkPoint fPos;
1611 GrColor fColor;
1612 SkPoint fOffset;
1613 SkScalar fOuterRadius;
1614 SkScalar fInnerRadius;
1615 SkScalar fOnAngle;
1616 SkScalar fTotalAngle;
1617 SkScalar fStartAngle;
1618 SkScalar fPhaseAngle;
1619 };
1620
1621 size_t vertexStride = gp->getVertexStride();
1622 SkASSERT(vertexStride == sizeof(CircleVertex));
1623
1624 const GrBuffer* vertexBuffer;
1625 int firstVertex;
1626 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1627 &firstVertex);
1628 if (!vertices) {
1629 SkDebugf("Could not allocate vertices\n");
1630 return;
1631 }
1632
1633 const GrBuffer* indexBuffer = nullptr;
1634 int firstIndex = 0;
1635 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1636 if (!indices) {
1637 SkDebugf("Could not allocate indices\n");
1638 return;
1639 }
1640
1641 int currStartVertex = 0;
1642 for (const auto& circle : fCircles) {
1643 // The inner radius in the vertex data must be specified in normalized space so that
1644 // length() can be called with smaller values to avoid precision issues with half
1645 // floats.
1646 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1647 const SkRect& bounds = circle.fDevBounds;
1648 bool reflect = false;
1649 SkScalar totalAngle = circle.fTotalAngle;
1650 if (totalAngle < 0) {
1651 reflect = true;
1652 totalAngle = -totalAngle;
1653 }
1654
1655 // The bounding geometry for the circle is composed of an outer bounding octagon and
1656 // an inner bounded octagon.
1657
1658 // Initializes the attributes that are the same at each vertex. Also applies reflection.
1659 auto init_const_attrs_and_reflect = [&](CircleVertex* v) {
1660 v->fColor = circle.fColor;
1661 v->fOuterRadius = circle.fOuterRadius;
1662 v->fInnerRadius = normInnerRadius;
1663 v->fOnAngle = circle.fOnAngle;
1664 v->fTotalAngle = totalAngle;
1665 v->fStartAngle = circle.fStartAngle;
1666 v->fPhaseAngle = circle.fPhaseAngle;
1667 if (reflect) {
1668 v->fStartAngle = -v->fStartAngle;
1669 v->fOffset.fY = -v->fOffset.fY;
1670 }
1671 };
1672
1673 // Compute the vertices of the outer octagon.
1674 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1675 SkScalar halfWidth = 0.5f * bounds.width();
1676 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
1677 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * vertexStride);
1678 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
1679 v->fOffset = {x, y};
1680 init_const_attrs_and_reflect(v);
1681 };
1682 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
1683 init_outer_vertex(0, -kOctOffset, -1);
1684 init_outer_vertex(1, kOctOffset, -1);
1685 init_outer_vertex(2, 1, -kOctOffset);
1686 init_outer_vertex(3, 1, kOctOffset);
1687 init_outer_vertex(4, kOctOffset, 1);
1688 init_outer_vertex(5, -kOctOffset, 1);
1689 init_outer_vertex(6, -1, kOctOffset);
1690 init_outer_vertex(7, -1, -kOctOffset);
1691
1692 // Compute the vertices of the inner octagon.
1693 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
1694 CircleVertex* v =
1695 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * vertexStride);
1696 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
1697 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
1698 init_const_attrs_and_reflect(v);
1699 };
1700
1701 // cosine and sine of pi/8
1702 static constexpr SkScalar kCos = 0.923579533f;
1703 static constexpr SkScalar kSin = 0.382683432f;
1704
1705 init_inner_vertex(0, -kSin, -kCos);
1706 init_inner_vertex(1, kSin, -kCos);
1707 init_inner_vertex(2, kCos, -kSin);
1708 init_inner_vertex(3, kCos, kSin);
1709 init_inner_vertex(4, kSin, kCos);
1710 init_inner_vertex(5, -kSin, kCos);
1711 init_inner_vertex(6, -kCos, kSin);
1712 init_inner_vertex(7, -kCos, -kSin);
1713
1714 const uint16_t* primIndices = circle_type_to_indices(true);
1715 const int primIndexCount = circle_type_to_index_count(true);
1716 for (int i = 0; i < primIndexCount; ++i) {
1717 *indices++ = primIndices[i] + currStartVertex;
1718 }
1719
1720 currStartVertex += circle_type_to_vert_count(true);
1721 vertices += circle_type_to_vert_count(true) * vertexStride;
1722 }
1723
1724 GrMesh mesh(GrPrimitiveType::kTriangles);
1725 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1726 mesh.setVertexData(vertexBuffer, firstVertex);
1727 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
1728 }
1729
1730 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1731 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1732
1733 // can only represent 65535 unique vertices with 16-bit indices
1734 if (fVertCount + that->fVertCount > 65536) {
1735 return false;
1736 }
1737
1738 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1739 return false;
1740 }
1741
1742 if (fHelper.usesLocalCoords() &&
1743 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1744 return false;
1745 }
1746
1747 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1748 this->joinBounds(*that);
1749 fVertCount += that->fVertCount;
1750 fIndexCount += that->fIndexCount;
1751 return true;
1752 }
1753
1754 struct Circle {
1755 GrColor fColor;
1756 SkScalar fOuterRadius;
1757 SkScalar fInnerRadius;
1758 SkScalar fOnAngle;
1759 SkScalar fTotalAngle;
1760 SkScalar fStartAngle;
1761 SkScalar fPhaseAngle;
1762 SkRect fDevBounds;
1763 };
1764
1765 SkMatrix fViewMatrixIfUsingLocalCoords;
1766 Helper fHelper;
1767 SkSTArray<1, Circle, true> fCircles;
1768 int fVertCount;
1769 int fIndexCount;
1770
1771 typedef GrMeshDrawOp INHERITED;
1772};
1773
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001774///////////////////////////////////////////////////////////////////////////////
1775
Brian Salomon05441c42017-05-15 16:45:49 -04001776class EllipseOp : public GrMeshDrawOp {
1777private:
1778 using Helper = GrSimpleMeshDrawOpHelper;
1779
1780 struct DeviceSpaceParams {
1781 SkPoint fCenter;
1782 SkScalar fXRadius;
1783 SkScalar fYRadius;
1784 SkScalar fInnerXRadius;
1785 SkScalar fInnerYRadius;
1786 };
1787
joshualitt76e7fb62015-02-11 08:52:27 -08001788public:
Brian Salomon25a88092016-12-01 09:36:50 -05001789 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001790
Brian Salomonea26d6b2018-01-23 20:33:21 +00001791 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04001792 const SkRect& ellipse, const SkStrokeRec& stroke) {
1793 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001794 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001795 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1796 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001797 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1798 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001799 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1800 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1801 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1802 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001803
bsalomon4b4a7cc2016-07-08 04:42:54 -07001804 // do (potentially) anisotropic mapping of stroke
1805 SkVector scaledStroke;
1806 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001807 scaledStroke.fX = SkScalarAbs(
1808 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1809 scaledStroke.fY = SkScalarAbs(
1810 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001811
1812 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001813 bool isStrokeOnly =
1814 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001815 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1816
Brian Salomon05441c42017-05-15 16:45:49 -04001817 params.fInnerXRadius = 0;
1818 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001819 if (hasStroke) {
1820 if (SkScalarNearlyZero(scaledStroke.length())) {
1821 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1822 } else {
1823 scaledStroke.scale(SK_ScalarHalf);
1824 }
1825
1826 // we only handle thick strokes for near-circular ellipses
1827 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001828 (0.5f * params.fXRadius > params.fYRadius ||
1829 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001830 return nullptr;
1831 }
1832
1833 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001834 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1835 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1836 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1837 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001838 return nullptr;
1839 }
1840
1841 // this is legit only if scale & translation (which should be the case at the moment)
1842 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001843 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1844 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001845 }
1846
Brian Salomon05441c42017-05-15 16:45:49 -04001847 params.fXRadius += scaledStroke.fX;
1848 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001849 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00001850 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001851 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852
Brian Salomonea26d6b2018-01-23 20:33:21 +00001853 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1854 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1855 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001856 SkStrokeRec::Style style = stroke.getStyle();
1857 bool isStrokeOnly =
1858 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001859
Brian Salomon05441c42017-05-15 16:45:49 -04001860 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1861 params.fInnerXRadius, params.fInnerYRadius,
1862 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1863 params.fCenter.fY - params.fYRadius,
1864 params.fCenter.fX + params.fXRadius,
1865 params.fCenter.fY + params.fYRadius)});
1866
1867 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001868
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001870 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001871
Brian Salomon05441c42017-05-15 16:45:49 -04001872 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1873 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001874 }
joshualitt76e7fb62015-02-11 08:52:27 -08001875
Brian Salomon289e3d82016-12-14 15:52:56 -05001876 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001877
Robert Phillipsf1748f52017-09-14 14:11:24 -04001878 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001879 fHelper.visitProxies(func);
1880 }
1881
Brian Salomon7c3e7182016-12-01 09:35:30 -05001882 SkString dumpInfo() const override {
1883 SkString string;
1884 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001885 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001886 string.appendf(
1887 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1888 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1889 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1890 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1891 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001892 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001893 string += fHelper.dumpInfo();
1894 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001895 return string;
1896 }
1897
Brian Osman9a725dd2017-09-20 09:53:22 -04001898 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1899 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001900 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001901 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1902 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001903 }
1904
1905 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1906
bsalomone46f9fe2015-08-18 06:05:14 -07001907private:
Brian Salomon91326c32017-08-09 16:02:19 -04001908 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001909 SkMatrix localMatrix;
1910 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001911 return;
1912 }
1913
1914 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00001915 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001916
bsalomonb5238a72015-05-05 07:49:49 -07001917 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001918 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001919 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001920 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1921 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001922 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001923 return;
1924 }
1925
Brian Salomon05441c42017-05-15 16:45:49 -04001926 for (const auto& ellipse : fEllipses) {
1927 GrColor color = ellipse.fColor;
1928 SkScalar xRadius = ellipse.fXRadius;
1929 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001930
1931 // Compute the reciprocals of the radii here to save time in the shader
1932 SkScalar xRadRecip = SkScalarInvert(xRadius);
1933 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001934 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1935 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001936
vjiaoblack977996d2016-06-30 12:20:54 -07001937 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1938 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1939 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1940
joshualitt76e7fb62015-02-11 08:52:27 -08001941 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001942 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001943 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001944 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001945 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1946 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1947
Brian Salomon05441c42017-05-15 16:45:49 -04001948 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001949 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001950 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001951 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1952 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1953
Brian Salomon57caa662017-10-18 12:21:05 +00001954 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001955 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001956 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001957 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1958 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1959
Brian Salomon57caa662017-10-18 12:21:05 +00001960 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001961 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001962 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001963 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1964 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1965
bsalomonb5238a72015-05-05 07:49:49 -07001966 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001967 }
Brian Salomon05441c42017-05-15 16:45:49 -04001968 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001969 }
1970
Brian Salomon25a88092016-12-01 09:36:50 -05001971 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001972 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001973
Brian Salomon05441c42017-05-15 16:45:49 -04001974 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001975 return false;
1976 }
1977
bsalomoncdaa97b2016-03-08 08:30:14 -08001978 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001979 return false;
1980 }
1981
Brian Salomon05441c42017-05-15 16:45:49 -04001982 if (fHelper.usesLocalCoords() &&
1983 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001984 return false;
1985 }
1986
Brian Salomon05441c42017-05-15 16:45:49 -04001987 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001988 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001989 return true;
1990 }
1991
Brian Salomon05441c42017-05-15 16:45:49 -04001992 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001993 GrColor fColor;
1994 SkScalar fXRadius;
1995 SkScalar fYRadius;
1996 SkScalar fInnerXRadius;
1997 SkScalar fInnerYRadius;
1998 SkRect fDevBounds;
1999 };
joshualitt76e7fb62015-02-11 08:52:27 -08002000
Brian Salomon289e3d82016-12-14 15:52:56 -05002001 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002002 Helper fHelper;
2003 bool fStroked;
2004 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002005
Brian Salomon05441c42017-05-15 16:45:49 -04002006 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002007};
2008
joshualitt76e7fb62015-02-11 08:52:27 -08002009/////////////////////////////////////////////////////////////////////////////////////////////////
2010
Brian Salomon05441c42017-05-15 16:45:49 -04002011class DIEllipseOp : public GrMeshDrawOp {
2012private:
2013 using Helper = GrSimpleMeshDrawOpHelper;
2014
2015 struct DeviceSpaceParams {
2016 SkPoint fCenter;
2017 SkScalar fXRadius;
2018 SkScalar fYRadius;
2019 SkScalar fInnerXRadius;
2020 SkScalar fInnerYRadius;
2021 DIEllipseStyle fStyle;
2022 };
2023
joshualitt76e7fb62015-02-11 08:52:27 -08002024public:
Brian Salomon25a88092016-12-01 09:36:50 -05002025 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002026
Brian Salomonea26d6b2018-01-23 20:33:21 +00002027 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002028 const SkRect& ellipse, const SkStrokeRec& stroke) {
2029 DeviceSpaceParams params;
2030 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2031 params.fXRadius = SkScalarHalf(ellipse.width());
2032 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002033
bsalomon4b4a7cc2016-07-08 04:42:54 -07002034 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002035 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2036 ? DIEllipseStyle::kStroke
2037 : (SkStrokeRec::kHairline_Style == style)
2038 ? DIEllipseStyle::kHairline
2039 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002040
Brian Salomon05441c42017-05-15 16:45:49 -04002041 params.fInnerXRadius = 0;
2042 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002043 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2044 SkScalar strokeWidth = stroke.getWidth();
2045
2046 if (SkScalarNearlyZero(strokeWidth)) {
2047 strokeWidth = SK_ScalarHalf;
2048 } else {
2049 strokeWidth *= SK_ScalarHalf;
2050 }
2051
2052 // we only handle thick strokes for near-circular ellipses
2053 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002054 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2055 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002056 return nullptr;
2057 }
2058
2059 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002060 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2061 (strokeWidth * strokeWidth) * params.fXRadius) {
2062 return nullptr;
2063 }
2064 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2065 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002066 return nullptr;
2067 }
2068
2069 // set inner radius (if needed)
2070 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002071 params.fInnerXRadius = params.fXRadius - strokeWidth;
2072 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002073 }
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075 params.fXRadius += strokeWidth;
2076 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002077 }
Brian Salomon05441c42017-05-15 16:45:49 -04002078 if (DIEllipseStyle::kStroke == params.fStyle &&
2079 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2080 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002081 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002082 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002083 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002084
Brian Salomonea26d6b2018-01-23 20:33:21 +00002085 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
2086 const SkMatrix& viewMatrix)
2087 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002088 // This expands the outer rect so that after CTM we end up with a half-pixel border
2089 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2090 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2091 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2092 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05002093 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2094 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002095
Brian Salomon05441c42017-05-15 16:45:49 -04002096 fEllipses.emplace_back(
2097 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2098 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2099 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2100 params.fCenter.fY - params.fYRadius - geoDy,
2101 params.fCenter.fX + params.fXRadius + geoDx,
2102 params.fCenter.fY + params.fYRadius + geoDy)});
2103 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2104 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002105 }
2106
Brian Salomon289e3d82016-12-14 15:52:56 -05002107 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002108
Robert Phillipsf1748f52017-09-14 14:11:24 -04002109 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002110 fHelper.visitProxies(func);
2111 }
2112
Brian Salomon7c3e7182016-12-01 09:35:30 -05002113 SkString dumpInfo() const override {
2114 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002115 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002116 string.appendf(
2117 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2118 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2119 "GeoDY: %.2f\n",
2120 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
2121 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2122 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002123 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002124 string += fHelper.dumpInfo();
2125 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002126 return string;
2127 }
2128
Brian Osman9a725dd2017-09-20 09:53:22 -04002129 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2130 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002131 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002132 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2133 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002134 }
2135
2136 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2137
bsalomone46f9fe2015-08-18 06:05:14 -07002138private:
Brian Salomon91326c32017-08-09 16:02:19 -04002139 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002140 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002141 sk_sp<GrGeometryProcessor> gp(
Brian Salomonea26d6b2018-01-23 20:33:21 +00002142 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002143
joshualitt76e7fb62015-02-11 08:52:27 -08002144 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08002145 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07002146 QuadHelper helper;
2147 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04002148 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002149 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002150 return;
2151 }
2152
Brian Salomon05441c42017-05-15 16:45:49 -04002153 for (const auto& ellipse : fEllipses) {
2154 GrColor color = ellipse.fColor;
2155 SkScalar xRadius = ellipse.fXRadius;
2156 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002157
Brian Salomon05441c42017-05-15 16:45:49 -04002158 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002159
2160 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002161 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2162 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002163
joshualitt76e7fb62015-02-11 08:52:27 -08002164 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002165 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002166 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002167 verts[0].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002168
Brian Salomon289e3d82016-12-14 15:52:56 -05002169 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002170 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002171 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002172 verts[1].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002173
Brian Salomon57caa662017-10-18 12:21:05 +00002174 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002175 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002176 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002177 verts[2].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002178
Brian Salomon57caa662017-10-18 12:21:05 +00002179 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002180 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002181 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002182 verts[3].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
2183
2184 if (DIEllipseStyle::kStroke == this->style()) {
2185 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
2186 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
2187
2188 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2189 -innerRatioY - offsetDy);
2190 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2191 innerRatioY + offsetDy);
2192 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2193 -innerRatioY - offsetDy);
2194 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2195 innerRatioY + offsetDy);
2196 }
joshualitt76e7fb62015-02-11 08:52:27 -08002197
bsalomonb5238a72015-05-05 07:49:49 -07002198 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002199 }
Brian Salomon05441c42017-05-15 16:45:49 -04002200 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002201 }
halcanary9d524f22016-03-29 09:03:52 -07002202
Brian Salomon25a88092016-12-01 09:36:50 -05002203 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002204 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002205 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002206 return false;
2207 }
2208
bsalomoncdaa97b2016-03-08 08:30:14 -08002209 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08002210 return false;
2211 }
2212
joshualittd96a67b2015-05-05 14:09:05 -07002213 // TODO rewrite to allow positioning on CPU
2214 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08002215 return false;
2216 }
2217
Brian Salomon05441c42017-05-15 16:45:49 -04002218 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002219 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002220 return true;
2221 }
2222
Brian Salomon05441c42017-05-15 16:45:49 -04002223 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2224 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002225
Brian Salomon05441c42017-05-15 16:45:49 -04002226 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002227 SkMatrix fViewMatrix;
2228 GrColor fColor;
2229 SkScalar fXRadius;
2230 SkScalar fYRadius;
2231 SkScalar fInnerXRadius;
2232 SkScalar fInnerYRadius;
2233 SkScalar fGeoDx;
2234 SkScalar fGeoDy;
2235 DIEllipseStyle fStyle;
2236 SkRect fBounds;
2237 };
2238
Brian Salomon05441c42017-05-15 16:45:49 -04002239 Helper fHelper;
2240 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002241
Brian Salomon05441c42017-05-15 16:45:49 -04002242 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002243};
2244
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002245///////////////////////////////////////////////////////////////////////////////
2246
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002247// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002248//
2249// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2250// ____________
2251// |_|________|_|
2252// | | | |
2253// | | | |
2254// | | | |
2255// |_|________|_|
2256// |_|________|_|
2257//
2258// For strokes, we don't draw the center quad.
2259//
2260// For circular roundrects, in the case where the stroke width is greater than twice
2261// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002262// in the center. The shared vertices are duplicated so we can set a different outer radius
2263// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002264// ____________
2265// |_|________|_|
2266// | |\ ____ /| |
2267// | | | | | |
2268// | | |____| | |
2269// |_|/______\|_|
2270// |_|________|_|
2271//
2272// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002273//
2274// For filled rrects that need to provide a distance vector we resuse the overstroke
2275// geometry but make the inner rect degenerate (either a point or a horizontal or
2276// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002277
jvanverth84839f62016-08-29 10:16:40 -07002278static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002279 // clang-format off
2280 // overstroke quads
2281 // we place this at the beginning so that we can skip these indices when rendering normally
2282 16, 17, 19, 16, 19, 18,
2283 19, 17, 23, 19, 23, 21,
2284 21, 23, 22, 21, 22, 20,
2285 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002286
Brian Salomon289e3d82016-12-14 15:52:56 -05002287 // corners
2288 0, 1, 5, 0, 5, 4,
2289 2, 3, 7, 2, 7, 6,
2290 8, 9, 13, 8, 13, 12,
2291 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002292
Brian Salomon289e3d82016-12-14 15:52:56 -05002293 // edges
2294 1, 2, 6, 1, 6, 5,
2295 4, 5, 9, 4, 9, 8,
2296 6, 7, 11, 6, 11, 10,
2297 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002298
Brian Salomon289e3d82016-12-14 15:52:56 -05002299 // center
2300 // we place this at the end so that we can ignore these indices when not rendering as filled
2301 5, 6, 10, 5, 10, 9,
2302 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002303};
Brian Salomon289e3d82016-12-14 15:52:56 -05002304
jvanverth84839f62016-08-29 10:16:40 -07002305// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002306static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002307
jvanverth84839f62016-08-29 10:16:40 -07002308// overstroke count is arraysize minus the center indices
2309static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2310// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002311static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002312// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002313static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2314static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002315static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002316
jvanverthc3d0e422016-08-25 08:12:35 -07002317enum RRectType {
2318 kFill_RRectType,
2319 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002320 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002321};
2322
jvanverth84839f62016-08-29 10:16:40 -07002323static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002324 switch (type) {
2325 case kFill_RRectType:
2326 case kStroke_RRectType:
2327 return kVertsPerStandardRRect;
2328 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002329 return kVertsPerOverstrokeRRect;
2330 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002331 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002332 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002333}
2334
2335static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002336 switch (type) {
2337 case kFill_RRectType:
2338 return kIndicesPerFillRRect;
2339 case kStroke_RRectType:
2340 return kIndicesPerStrokeRRect;
2341 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002342 return kIndicesPerOverstrokeRRect;
2343 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002344 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002345 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002346}
2347
2348static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002349 switch (type) {
2350 case kFill_RRectType:
2351 case kStroke_RRectType:
2352 return gStandardRRectIndices;
2353 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002354 return gOverstrokeRRectIndices;
2355 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002356 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002357 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002358}
2359
joshualitt76e7fb62015-02-11 08:52:27 -08002360///////////////////////////////////////////////////////////////////////////////////////////////////
2361
Robert Phillips79839d42016-10-06 15:03:34 -04002362// For distance computations in the interior of filled rrects we:
2363//
2364// add a interior degenerate (point or line) rect
2365// each vertex of that rect gets -outerRad as its radius
2366// this makes the computation of the distance to the outer edge be negative
2367// negative values are caught and then handled differently in the GP's onEmitCode
2368// each vertex is also given the normalized x & y distance from the interior rect's edge
2369// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2370
Brian Salomon05441c42017-05-15 16:45:49 -04002371class CircularRRectOp : public GrMeshDrawOp {
2372private:
2373 using Helper = GrSimpleMeshDrawOpHelper;
2374
joshualitt76e7fb62015-02-11 08:52:27 -08002375public:
Brian Salomon25a88092016-12-01 09:36:50 -05002376 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002377
bsalomon4b4a7cc2016-07-08 04:42:54 -07002378 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2379 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002380 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomone23bffd2017-06-02 11:01:10 -04002381 const SkRect& devRect, float devRadius,
2382 float devStrokeWidth, bool strokeOnly) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00002383 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomone23bffd2017-06-02 11:01:10 -04002384 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002385 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002386 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
2387 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002388 : INHERITED(ClassID())
2389 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002390 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002391 SkRect bounds = devRect;
2392 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2393 SkScalar innerRadius = 0.0f;
2394 SkScalar outerRadius = devRadius;
2395 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002396 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002397 if (devStrokeWidth > 0) {
2398 if (SkScalarNearlyZero(devStrokeWidth)) {
2399 halfWidth = SK_ScalarHalf;
2400 } else {
2401 halfWidth = SkScalarHalf(devStrokeWidth);
2402 }
joshualitt76e7fb62015-02-11 08:52:27 -08002403
bsalomon4b4a7cc2016-07-08 04:42:54 -07002404 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002405 // Outset stroke by 1/4 pixel
2406 devStrokeWidth += 0.25f;
2407 // If stroke is greater than width or height, this is still a fill
2408 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002409 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002410 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002411 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002412 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002413 }
2414 outerRadius += halfWidth;
2415 bounds.outset(halfWidth, halfWidth);
2416 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002417
bsalomon4b4a7cc2016-07-08 04:42:54 -07002418 // The radii are outset for two reasons. First, it allows the shader to simply perform
2419 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2420 // Second, the outer radius is used to compute the verts of the bounding box that is
2421 // rendered and the outset ensures the box will cover all partially covered by the rrect
2422 // corners.
2423 outerRadius += SK_ScalarHalf;
2424 innerRadius -= SK_ScalarHalf;
2425
bsalomon88cf17d2016-07-08 06:40:56 -07002426 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2427
2428 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002429 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2430
Brian Salomon05441c42017-05-15 16:45:49 -04002431 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002432 fVertCount = rrect_type_to_vert_count(type);
2433 fIndexCount = rrect_type_to_index_count(type);
2434 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002435 }
2436
Brian Salomon289e3d82016-12-14 15:52:56 -05002437 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002438
Robert Phillipsf1748f52017-09-14 14:11:24 -04002439 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002440 fHelper.visitProxies(func);
2441 }
2442
jvanverthc3d0e422016-08-25 08:12:35 -07002443 SkString dumpInfo() const override {
2444 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002445 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002446 string.appendf(
2447 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2448 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04002449 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
2450 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
2451 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002452 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002453 string += fHelper.dumpInfo();
2454 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002455 return string;
2456 }
2457
Brian Osman9a725dd2017-09-20 09:53:22 -04002458 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2459 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002460 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002461 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2462 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002463 }
2464
2465 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2466
Brian Salomon92aee3d2016-12-21 09:20:25 -05002467private:
Robert Phillips79839d42016-10-06 15:03:34 -04002468 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05002469 SkPoint fPos;
2470 GrColor fColor;
2471 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04002472 SkScalar fOuterRadius;
2473 SkScalar fInnerRadius;
2474 // No half plane, we don't use it here.
2475 };
2476
Brian Salomon289e3d82016-12-14 15:52:56 -05002477 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
2478 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2479 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002480 SkASSERT(smInset < bigInset);
2481
2482 // TL
2483 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
2484 (*verts)->fColor = color;
2485 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2486 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002487 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002488 (*verts)++;
2489
2490 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05002491 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002492 (*verts)->fColor = color;
2493 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2494 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002495 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002496 (*verts)++;
2497
Brian Salomon289e3d82016-12-14 15:52:56 -05002498 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002499 (*verts)->fColor = color;
2500 (*verts)->fOffset = SkPoint::Make(0, 0);
2501 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002502 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002503 (*verts)++;
2504
Brian Salomon289e3d82016-12-14 15:52:56 -05002505 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002506 (*verts)->fColor = color;
2507 (*verts)->fOffset = SkPoint::Make(0, 0);
2508 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002509 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002510 (*verts)++;
2511
2512 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
2513 (*verts)->fColor = color;
2514 (*verts)->fOffset = SkPoint::Make(0, 0);
2515 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002516 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002517 (*verts)++;
2518
2519 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
2520 (*verts)->fColor = color;
2521 (*verts)->fOffset = SkPoint::Make(0, 0);
2522 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002523 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002524 (*verts)++;
2525
2526 // BL
2527 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
2528 (*verts)->fColor = color;
2529 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2530 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002531 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002532 (*verts)++;
2533
2534 // BR
2535 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
2536 (*verts)->fColor = color;
2537 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2538 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002539 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002540 (*verts)++;
2541 }
2542
Brian Salomon91326c32017-08-09 16:02:19 -04002543 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002544 // Invert the view matrix as a local matrix (if any other processors require coords).
2545 SkMatrix localMatrix;
2546 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002547 return;
2548 }
2549
2550 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002551 sk_sp<GrGeometryProcessor> gp(
Brian Salomon45c92202018-04-10 10:53:58 -04002552 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002553
joshualitt76e7fb62015-02-11 08:52:27 -08002554 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07002555 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08002556
jvanverth84839f62016-08-29 10:16:40 -07002557 const GrBuffer* vertexBuffer;
2558 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002559
Brian Salomon289e3d82016-12-14 15:52:56 -05002560 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
2561 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07002562 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002563 SkDebugf("Could not allocate vertices\n");
2564 return;
2565 }
2566
jvanverth84839f62016-08-29 10:16:40 -07002567 const GrBuffer* indexBuffer = nullptr;
2568 int firstIndex = 0;
2569 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2570 if (!indices) {
2571 SkDebugf("Could not allocate indices\n");
2572 return;
2573 }
2574
2575 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002576 for (const auto& rrect : fRRects) {
2577 GrColor color = rrect.fColor;
2578 SkScalar outerRadius = rrect.fOuterRadius;
2579 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002580
Brian Salomon289e3d82016-12-14 15:52:56 -05002581 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2582 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002583
Brian Salomon289e3d82016-12-14 15:52:56 -05002584 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002585 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002586 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002587 SkScalar innerRadius = rrect.fType != kFill_RRectType
2588 ? rrect.fInnerRadius / rrect.fOuterRadius
2589 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002590 for (int i = 0; i < 4; ++i) {
2591 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002592 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002593 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
2594 verts->fOuterRadius = outerRadius;
2595 verts->fInnerRadius = innerRadius;
2596 verts++;
2597
2598 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002599 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002600 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2601 verts->fOuterRadius = outerRadius;
2602 verts->fInnerRadius = innerRadius;
2603 verts++;
2604
2605 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002606 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002607 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2608 verts->fOuterRadius = outerRadius;
2609 verts->fInnerRadius = innerRadius;
2610 verts++;
2611
2612 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002613 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002614 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
2615 verts->fOuterRadius = outerRadius;
2616 verts->fInnerRadius = innerRadius;
2617 verts++;
2618 }
jvanverthc3d0e422016-08-25 08:12:35 -07002619 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002620 // Effectively this is an additional stroked rrect, with its
2621 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2622 // This will give us correct AA in the center and the correct
2623 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002624 //
jvanvertha4f1af82016-08-29 07:17:47 -07002625 // Also, the outer offset is a constant vector pointing to the right, which
2626 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002627 if (kOverstroke_RRectType == rrect.fType) {
2628 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002629
Brian Salomon05441c42017-05-15 16:45:49 -04002630 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002631 // this is the normalized distance from the outer rectangle of this
2632 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002633 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002634
Brian Salomon289e3d82016-12-14 15:52:56 -05002635 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002636 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002637 }
jvanverth6a397612016-08-26 08:15:33 -07002638
Brian Salomon05441c42017-05-15 16:45:49 -04002639 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2640 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002641 for (int i = 0; i < primIndexCount; ++i) {
2642 *indices++ = primIndices[i] + currStartVertex;
2643 }
2644
Brian Salomon05441c42017-05-15 16:45:49 -04002645 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002646 }
2647
Chris Dalton3809bab2017-06-13 10:55:06 -06002648 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06002649 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
2650 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04002651 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002652 }
2653
Brian Salomon25a88092016-12-01 09:36:50 -05002654 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002655 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002656
2657 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002658 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002659 return false;
2660 }
2661
Brian Salomon05441c42017-05-15 16:45:49 -04002662 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002663 return false;
2664 }
2665
Brian Salomon05441c42017-05-15 16:45:49 -04002666 if (fHelper.usesLocalCoords() &&
2667 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002668 return false;
2669 }
2670
Brian Salomon05441c42017-05-15 16:45:49 -04002671 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002672 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002673 fVertCount += that->fVertCount;
2674 fIndexCount += that->fIndexCount;
2675 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002676 return true;
2677 }
2678
Brian Salomon05441c42017-05-15 16:45:49 -04002679 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002680 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002681 SkScalar fInnerRadius;
2682 SkScalar fOuterRadius;
2683 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002684 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002685 };
2686
Brian Salomon289e3d82016-12-14 15:52:56 -05002687 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002688 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002689 int fVertCount;
2690 int fIndexCount;
2691 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002692 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002693
Brian Salomon05441c42017-05-15 16:45:49 -04002694 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002695};
2696
jvanverth84839f62016-08-29 10:16:40 -07002697static const int kNumRRectsInIndexBuffer = 256;
2698
2699GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2700GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002701static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2702 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002703 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2704 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2705 switch (type) {
2706 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002707 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002708 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2709 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002710 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002711 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002712 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2713 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002714 default:
2715 SkASSERT(false);
2716 return nullptr;
2717 };
2718}
2719
Brian Salomon05441c42017-05-15 16:45:49 -04002720class EllipticalRRectOp : public GrMeshDrawOp {
2721private:
2722 using Helper = GrSimpleMeshDrawOpHelper;
2723
joshualitt76e7fb62015-02-11 08:52:27 -08002724public:
Brian Salomon25a88092016-12-01 09:36:50 -05002725 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002726
bsalomon4b4a7cc2016-07-08 04:42:54 -07002727 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2728 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002729 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002730 const SkRect& devRect, float devXRadius, float devYRadius,
2731 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002732 SkASSERT(devXRadius > 0.5);
2733 SkASSERT(devYRadius > 0.5);
2734 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2735 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002736 if (devStrokeWidths.fX > 0) {
2737 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2738 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2739 } else {
2740 devStrokeWidths.scale(SK_ScalarHalf);
2741 }
joshualitt76e7fb62015-02-11 08:52:27 -08002742
bsalomon4b4a7cc2016-07-08 04:42:54 -07002743 // we only handle thick strokes for near-circular ellipses
2744 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002745 (SK_ScalarHalf * devXRadius > devYRadius ||
2746 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002747 return nullptr;
2748 }
2749
2750 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002751 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2752 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002753 return nullptr;
2754 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002755 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2756 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002757 return nullptr;
2758 }
Brian Salomon05441c42017-05-15 16:45:49 -04002759 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002760 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002761 devXRadius, devYRadius, devStrokeWidths,
2762 strokeOnly);
2763 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002764
Brian Salomonea26d6b2018-01-23 20:33:21 +00002765 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2766 const SkRect& devRect, float devXRadius, float devYRadius,
2767 SkVector devStrokeHalfWidths, bool strokeOnly)
2768 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002769 SkScalar innerXRadius = 0.0f;
2770 SkScalar innerYRadius = 0.0f;
2771 SkRect bounds = devRect;
2772 bool stroked = false;
2773 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002774 // this is legit only if scale & translation (which should be the case at the moment)
2775 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002776 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2777 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002778 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2779 }
2780
Brian Salomon05441c42017-05-15 16:45:49 -04002781 devXRadius += devStrokeHalfWidths.fX;
2782 devYRadius += devStrokeHalfWidths.fY;
2783 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002784 }
2785
Brian Salomon05441c42017-05-15 16:45:49 -04002786 fStroked = stroked;
2787 fViewMatrixIfUsingLocalCoords = viewMatrix;
2788 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002789 // Expand the rect for aa in order to generate the correct vertices.
2790 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002791 fRRects.emplace_back(
2792 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002793 }
2794
Brian Salomon289e3d82016-12-14 15:52:56 -05002795 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002796
Robert Phillipsf1748f52017-09-14 14:11:24 -04002797 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002798 fHelper.visitProxies(func);
2799 }
2800
Brian Salomon7c3e7182016-12-01 09:35:30 -05002801 SkString dumpInfo() const override {
2802 SkString string;
2803 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002804 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002805 string.appendf(
2806 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2807 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2808 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2809 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2810 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002811 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002812 string += fHelper.dumpInfo();
2813 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002814 return string;
2815 }
2816
Brian Osman9a725dd2017-09-20 09:53:22 -04002817 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2818 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002819 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002820 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2821 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002822 }
2823
2824 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2825
bsalomone46f9fe2015-08-18 06:05:14 -07002826private:
Brian Salomon91326c32017-08-09 16:02:19 -04002827 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002828 SkMatrix localMatrix;
2829 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002830 return;
2831 }
2832
2833 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00002834 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002835
joshualitt76e7fb62015-02-11 08:52:27 -08002836 size_t vertexStride = gp->getVertexStride();
2837 SkASSERT(vertexStride == sizeof(EllipseVertex));
2838
bsalomonb5238a72015-05-05 07:49:49 -07002839 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002840 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002841 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2842 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002843
Chris Dalton3809bab2017-06-13 10:55:06 -06002844 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002845 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002846 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002847 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002848 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002849 SkDebugf("Could not allocate vertices\n");
2850 return;
2851 }
2852
Brian Salomon05441c42017-05-15 16:45:49 -04002853 for (const auto& rrect : fRRects) {
2854 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002855 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002856 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2857 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2858 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2859 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002860
2861 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002862 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2863 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002864
Brian Salomon05441c42017-05-15 16:45:49 -04002865 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002866
Brian Salomon289e3d82016-12-14 15:52:56 -05002867 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2868 bounds.fBottom - yOuterRadius, bounds.fBottom};
2869 SkScalar yOuterOffsets[4] = {yOuterRadius,
2870 SK_ScalarNearlyZero, // we're using inversesqrt() in
2871 // shader, so can't be exactly 0
2872 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002873
2874 for (int i = 0; i < 4; ++i) {
2875 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002876 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002877 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2878 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2879 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2880 verts++;
2881
2882 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002883 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002884 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2885 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2886 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2887 verts++;
2888
2889 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002890 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002891 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2892 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2893 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2894 verts++;
2895
2896 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002897 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002898 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2899 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2900 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2901 verts++;
2902 }
2903 }
Brian Salomon05441c42017-05-15 16:45:49 -04002904 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002905 }
2906
Brian Salomon25a88092016-12-01 09:36:50 -05002907 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002908 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002909
Brian Salomon05441c42017-05-15 16:45:49 -04002910 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002911 return false;
2912 }
2913
bsalomoncdaa97b2016-03-08 08:30:14 -08002914 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002915 return false;
2916 }
2917
Brian Salomon05441c42017-05-15 16:45:49 -04002918 if (fHelper.usesLocalCoords() &&
2919 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002920 return false;
2921 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002922
Brian Salomon05441c42017-05-15 16:45:49 -04002923 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002924 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002925 return true;
2926 }
2927
Brian Salomon05441c42017-05-15 16:45:49 -04002928 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002929 GrColor fColor;
2930 SkScalar fXRadius;
2931 SkScalar fYRadius;
2932 SkScalar fInnerXRadius;
2933 SkScalar fInnerYRadius;
2934 SkRect fDevBounds;
2935 };
2936
Brian Salomon289e3d82016-12-14 15:52:56 -05002937 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002938 Helper fHelper;
2939 bool fStroked;
2940 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002941
Brian Salomon05441c42017-05-15 16:45:49 -04002942 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002943};
2944
Brian Salomon05441c42017-05-15 16:45:49 -04002945static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002946 const SkMatrix& viewMatrix,
2947 const SkRRect& rrect,
2948 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002949 SkASSERT(viewMatrix.rectStaysRect());
2950 SkASSERT(rrect.isSimple());
2951 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002952
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002953 // RRect ops only handle simple, but not too simple, rrects.
2954 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002955 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002956 SkRect bounds;
2957 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002958
Mike Reed242135a2018-02-22 13:41:39 -05002959 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05002960 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2961 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2962 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2963 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002964
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002965 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002966
bsalomon4b4a7cc2016-07-08 04:42:54 -07002967 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2968 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002969 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002970
Brian Salomon289e3d82016-12-14 15:52:56 -05002971 bool isStrokeOnly =
2972 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002973 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2974
jvanverthc3d0e422016-08-25 08:12:35 -07002975 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002976 if (hasStroke) {
2977 if (SkStrokeRec::kHairline_Style == style) {
2978 scaledStroke.set(1, 1);
2979 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002980 scaledStroke.fX = SkScalarAbs(
2981 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2982 scaledStroke.fY = SkScalarAbs(
2983 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002984 }
2985
jvanverthc3d0e422016-08-25 08:12:35 -07002986 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2987 // for non-circular rrects, if half of strokewidth is greater than radius,
2988 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002989 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2990 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002991 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002992 }
2993 }
2994
2995 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2996 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2997 // patch will have fractional coverage. This only matters when the interior is actually filled.
2998 // We could consider falling back to rect rendering here, since a tiny radius is
2999 // indistinguishable from a square corner.
3000 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003001 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003002 }
3003
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003004 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003005 if (isCircular) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003006 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
3007 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003008 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003009 } else {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003010 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
Brian Salomon05441c42017-05-15 16:45:49 -04003011 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003012 }
joshualitt3e708c52015-04-30 13:49:27 -07003013}
3014
Brian Salomonea26d6b2018-01-23 20:33:21 +00003015std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
3016 const SkMatrix& viewMatrix,
3017 const SkRRect& rrect,
3018 const SkStrokeRec& stroke,
3019 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003020 if (rrect.isOval()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003021 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), GrStyle(stroke, nullptr),
3022 shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003023 }
3024
3025 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003026 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003027 }
3028
Brian Salomonea26d6b2018-01-23 20:33:21 +00003029 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003030}
joshualitt3e708c52015-04-30 13:49:27 -07003031
bsalomon4b4a7cc2016-07-08 04:42:54 -07003032///////////////////////////////////////////////////////////////////////////////
3033
Brian Salomonea26d6b2018-01-23 20:33:21 +00003034std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
3035 const SkMatrix& viewMatrix,
3036 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003037 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003038 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003039 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003040 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003041 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3042 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003043 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003044 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003045 if (style.hasNonDashPathEffect()) {
3046 return nullptr;
3047 } else if (style.isDashed()) {
3048 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3049 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3050 return nullptr;
3051 }
3052 auto onInterval = style.dashIntervals()[0];
3053 auto offInterval = style.dashIntervals()[1];
3054 if (offInterval == 0) {
3055 GrStyle strokeStyle(style.strokeRec(), nullptr);
3056 return MakeOvalOp(std::move(paint), viewMatrix, oval, strokeStyle, shaderCaps);
3057 } else if (onInterval == 0) {
3058 // There is nothing to draw but we have no way to indicate that here.
3059 return nullptr;
3060 }
3061 auto angularOnInterval = onInterval / r;
3062 auto angularOffInterval = offInterval / r;
3063 auto phaseAngle = style.dashPhase() / r;
3064 // Currently this function doesn't accept ovals with different start angles, though
3065 // it could.
3066 static const SkScalar kStartAngle = 0.f;
3067 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, r,
3068 style.strokeRec().getWidth(), kStartAngle,
3069 angularOnInterval, angularOffInterval, phaseAngle);
3070 }
3071 return CircleOp::Make(std::move(paint), viewMatrix, center, r, style);
3072 }
3073
3074 if (style.pathEffect()) {
3075 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003076 }
3077
Stan Ilieveb868aa2017-02-21 11:06:16 -05003078 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003079 if (viewMatrix.rectStaysRect()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003080 return EllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003081 }
3082
Stan Ilieveb868aa2017-02-21 11:06:16 -05003083 // Otherwise, if we have shader derivative support, render as device-independent
3084 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003085 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
Stan Ilieveb868aa2017-02-21 11:06:16 -05003086 }
3087
bsalomon4b4a7cc2016-07-08 04:42:54 -07003088 return nullptr;
3089}
3090
3091///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003092
Brian Salomonea26d6b2018-01-23 20:33:21 +00003093std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
3094 const SkRect& oval, SkScalar startAngle,
3095 SkScalar sweepAngle, bool useCenter,
3096 const GrStyle& style,
3097 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003098 SkASSERT(!oval.isEmpty());
3099 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003100 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003101 if (SkScalarAbs(sweepAngle) >= 360.f) {
3102 return nullptr;
3103 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003104 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3105 return nullptr;
3106 }
3107 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003108 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3109 useCenter};
Brian Salomonea26d6b2018-01-23 20:33:21 +00003110 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003111}
3112
3113///////////////////////////////////////////////////////////////////////////////
3114
Hal Canary6f6961e2017-01-31 13:50:44 -05003115#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003116
Brian Salomon05441c42017-05-15 16:45:49 -04003117GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003118 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003119 SkScalar rotate = random->nextSScalar1() * 360.f;
3120 SkScalar translateX = random->nextSScalar1() * 1000.f;
3121 SkScalar translateY = random->nextSScalar1() * 1000.f;
3122 SkScalar scale = random->nextSScalar1() * 100.f;
3123 SkMatrix viewMatrix;
3124 viewMatrix.setRotate(rotate);
3125 viewMatrix.postTranslate(translateX, translateY);
3126 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003127 SkRect circle = GrTest::TestSquare(random);
3128 SkPoint center = {circle.centerX(), circle.centerY()};
3129 SkScalar radius = circle.width() / 2.f;
3130 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003131 CircleOp::ArcParams arcParamsTmp;
3132 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003133 if (random->nextBool()) {
3134 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003135 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3136 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003137 arcParams = &arcParamsTmp;
3138 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003139 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
3140 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003141 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003142 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003143 }
3144 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003145}
3146
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003147GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3148 SkScalar rotate = random->nextSScalar1() * 360.f;
3149 SkScalar translateX = random->nextSScalar1() * 1000.f;
3150 SkScalar translateY = random->nextSScalar1() * 1000.f;
3151 SkScalar scale = random->nextSScalar1() * 100.f;
3152 SkMatrix viewMatrix;
3153 viewMatrix.setRotate(rotate);
3154 viewMatrix.postTranslate(translateX, translateY);
3155 viewMatrix.postScale(scale, scale);
3156 SkRect circle = GrTest::TestSquare(random);
3157 SkPoint center = {circle.centerX(), circle.centerY()};
3158 SkScalar radius = circle.width() / 2.f;
3159 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3160 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3161 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3162 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3163 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
3164 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, radius, strokeWidth,
3165 startAngle, onAngle, offAngle, phase);
3166}
3167
Brian Salomon05441c42017-05-15 16:45:49 -04003168GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003169 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003170 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003171 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003172}
3173
Brian Salomon05441c42017-05-15 16:45:49 -04003174GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003175 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003176 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003177 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003178}
3179
Brian Salomon05441c42017-05-15 16:45:49 -04003180GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003181 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003182 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003183 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003184}
3185
3186#endif