blob: f6a2a1a087744ce42d381dca4f7dd6aa8b732f90 [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);
Brian Salomon802cb312018-06-08 18:05:20 -04001416 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1417 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06001418 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001419 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001420 }
1421
Brian Salomon25a88092016-12-01 09:36:50 -05001422 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001423 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001424
1425 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001426 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001427 return false;
1428 }
1429
Brian Salomon05441c42017-05-15 16:45:49 -04001430 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001431 return false;
1432 }
1433
Brian Salomon05441c42017-05-15 16:45:49 -04001434 if (fHelper.usesLocalCoords() &&
1435 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001436 return false;
1437 }
1438
Brian Salomon289e3d82016-12-14 15:52:56 -05001439 // Because we've set up the ops that don't use the planes with noop values
1440 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001441 fClipPlane |= that->fClipPlane;
1442 fClipPlaneIsect |= that->fClipPlaneIsect;
1443 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001444 fRoundCaps |= that->fRoundCaps;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001445
Brian Salomon05441c42017-05-15 16:45:49 -04001446 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001447 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001448 fVertCount += that->fVertCount;
1449 fIndexCount += that->fIndexCount;
1450 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001451 return true;
1452 }
1453
Brian Salomon05441c42017-05-15 16:45:49 -04001454 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001455 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001456 SkScalar fInnerRadius;
1457 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001458 SkScalar fClipPlane[3];
1459 SkScalar fIsectPlane[3];
1460 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001461 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001462 SkRect fDevBounds;
1463 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001464 };
1465
Brian Salomon289e3d82016-12-14 15:52:56 -05001466 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001467 Helper fHelper;
1468 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001469 int fVertCount;
1470 int fIndexCount;
1471 bool fAllFill;
1472 bool fClipPlane;
1473 bool fClipPlaneIsect;
1474 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001475 bool fRoundCaps;
reed1b55a962015-09-17 20:16:13 -07001476
Brian Salomon05441c42017-05-15 16:45:49 -04001477 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001478};
1479
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001480class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1481private:
1482 using Helper = GrSimpleMeshDrawOpHelper;
1483
1484public:
1485 DEFINE_OP_CLASS_ID
1486
1487 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1488 SkPoint center, SkScalar radius, SkScalar strokeWidth,
1489 SkScalar startAngle, SkScalar onAngle, SkScalar offAngle,
1490 SkScalar phaseAngle) {
1491 SkASSERT(circle_stays_circle(viewMatrix));
1492 SkASSERT(strokeWidth < 2 * radius);
1493 return Helper::FactoryHelper<ButtCapDashedCircleOp>(std::move(paint), viewMatrix, center,
1494 radius, strokeWidth, startAngle,
1495 onAngle, offAngle, phaseAngle);
1496 }
1497
1498 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color,
1499 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1500 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1501 SkScalar offAngle, SkScalar phaseAngle)
1502 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1503 SkASSERT(circle_stays_circle(viewMatrix));
1504 viewMatrix.mapPoints(&center, 1);
1505 radius = viewMatrix.mapRadius(radius);
1506 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1507
1508 // Determine the angle where the circle starts in device space and whether its orientation
1509 // has been reversed.
1510 SkVector start;
1511 bool reflection;
1512 if (!startAngle) {
1513 start = {1, 0};
1514 } else {
1515 start.fY = SkScalarSinCos(startAngle, &start.fX);
1516 }
1517 viewMatrix.mapVectors(&start, 1);
1518 startAngle = SkScalarATan2(start.fY, start.fX);
1519 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1520 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1521
1522 auto totalAngle = onAngle + offAngle;
1523 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1524
1525 SkScalar halfWidth = 0;
1526 if (SkScalarNearlyZero(strokeWidth)) {
1527 halfWidth = SK_ScalarHalf;
1528 } else {
1529 halfWidth = SkScalarHalf(strokeWidth);
1530 }
1531
1532 SkScalar outerRadius = radius + halfWidth;
1533 SkScalar innerRadius = radius - halfWidth;
1534
1535 // The radii are outset for two reasons. First, it allows the shader to simply perform
1536 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1537 // Second, the outer radius is used to compute the verts of the bounding box that is
1538 // rendered and the outset ensures the box will cover all partially covered by the circle.
1539 outerRadius += SK_ScalarHalf;
1540 innerRadius -= SK_ScalarHalf;
1541 fViewMatrixIfUsingLocalCoords = viewMatrix;
1542
1543 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1544 center.fX + outerRadius, center.fY + outerRadius);
1545
1546 // We store whether there is a reflection as a negative total angle.
1547 if (reflection) {
1548 totalAngle = -totalAngle;
1549 }
1550 fCircles.push_back(Circle{
1551 color,
1552 outerRadius,
1553 innerRadius,
1554 onAngle,
1555 totalAngle,
1556 startAngle,
1557 phaseAngle,
1558 devBounds
1559 });
1560 // Use the original radius and stroke radius for the bounds so that it does not include the
1561 // AA bloat.
1562 radius += halfWidth;
1563 this->setBounds(
1564 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1565 HasAABloat::kYes, IsZeroArea::kNo);
1566 fVertCount = circle_type_to_vert_count(true);
1567 fIndexCount = circle_type_to_index_count(true);
1568 }
1569
1570 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1571
1572 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); }
1573
1574 SkString dumpInfo() const override {
1575 SkString string;
1576 for (int i = 0; i < fCircles.count(); ++i) {
1577 string.appendf(
1578 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1579 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1580 "Phase: %.2f\n",
1581 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1582 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1583 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle,
1584 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle);
1585 }
1586 string += fHelper.dumpInfo();
1587 string += INHERITED::dumpInfo();
1588 return string;
1589 }
1590
1591 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1592 GrPixelConfigIsClamped dstIsClamped) override {
1593 GrColor* color = &fCircles.front().fColor;
1594 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1595 GrProcessorAnalysisCoverage::kSingleChannel, color);
1596 }
1597
1598 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1599
1600private:
1601 void onPrepareDraws(Target* target) override {
1602 SkMatrix localMatrix;
1603 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1604 return;
1605 }
1606
1607 // Setup geometry processor
1608 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix));
1609
1610 struct CircleVertex {
1611 SkPoint fPos;
1612 GrColor fColor;
1613 SkPoint fOffset;
1614 SkScalar fOuterRadius;
1615 SkScalar fInnerRadius;
1616 SkScalar fOnAngle;
1617 SkScalar fTotalAngle;
1618 SkScalar fStartAngle;
1619 SkScalar fPhaseAngle;
1620 };
1621
1622 size_t vertexStride = gp->getVertexStride();
1623 SkASSERT(vertexStride == sizeof(CircleVertex));
1624
1625 const GrBuffer* vertexBuffer;
1626 int firstVertex;
1627 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1628 &firstVertex);
1629 if (!vertices) {
1630 SkDebugf("Could not allocate vertices\n");
1631 return;
1632 }
1633
1634 const GrBuffer* indexBuffer = nullptr;
1635 int firstIndex = 0;
1636 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1637 if (!indices) {
1638 SkDebugf("Could not allocate indices\n");
1639 return;
1640 }
1641
1642 int currStartVertex = 0;
1643 for (const auto& circle : fCircles) {
1644 // The inner radius in the vertex data must be specified in normalized space so that
1645 // length() can be called with smaller values to avoid precision issues with half
1646 // floats.
1647 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1648 const SkRect& bounds = circle.fDevBounds;
1649 bool reflect = false;
1650 SkScalar totalAngle = circle.fTotalAngle;
1651 if (totalAngle < 0) {
1652 reflect = true;
1653 totalAngle = -totalAngle;
1654 }
1655
1656 // The bounding geometry for the circle is composed of an outer bounding octagon and
1657 // an inner bounded octagon.
1658
1659 // Initializes the attributes that are the same at each vertex. Also applies reflection.
1660 auto init_const_attrs_and_reflect = [&](CircleVertex* v) {
1661 v->fColor = circle.fColor;
1662 v->fOuterRadius = circle.fOuterRadius;
1663 v->fInnerRadius = normInnerRadius;
1664 v->fOnAngle = circle.fOnAngle;
1665 v->fTotalAngle = totalAngle;
1666 v->fStartAngle = circle.fStartAngle;
1667 v->fPhaseAngle = circle.fPhaseAngle;
1668 if (reflect) {
1669 v->fStartAngle = -v->fStartAngle;
1670 v->fOffset.fY = -v->fOffset.fY;
1671 }
1672 };
1673
1674 // Compute the vertices of the outer octagon.
1675 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1676 SkScalar halfWidth = 0.5f * bounds.width();
1677 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
1678 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * vertexStride);
1679 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
1680 v->fOffset = {x, y};
1681 init_const_attrs_and_reflect(v);
1682 };
1683 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
1684 init_outer_vertex(0, -kOctOffset, -1);
1685 init_outer_vertex(1, kOctOffset, -1);
1686 init_outer_vertex(2, 1, -kOctOffset);
1687 init_outer_vertex(3, 1, kOctOffset);
1688 init_outer_vertex(4, kOctOffset, 1);
1689 init_outer_vertex(5, -kOctOffset, 1);
1690 init_outer_vertex(6, -1, kOctOffset);
1691 init_outer_vertex(7, -1, -kOctOffset);
1692
1693 // Compute the vertices of the inner octagon.
1694 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
1695 CircleVertex* v =
1696 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * vertexStride);
1697 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
1698 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
1699 init_const_attrs_and_reflect(v);
1700 };
1701
1702 // cosine and sine of pi/8
1703 static constexpr SkScalar kCos = 0.923579533f;
1704 static constexpr SkScalar kSin = 0.382683432f;
1705
1706 init_inner_vertex(0, -kSin, -kCos);
1707 init_inner_vertex(1, kSin, -kCos);
1708 init_inner_vertex(2, kCos, -kSin);
1709 init_inner_vertex(3, kCos, kSin);
1710 init_inner_vertex(4, kSin, kCos);
1711 init_inner_vertex(5, -kSin, kCos);
1712 init_inner_vertex(6, -kCos, kSin);
1713 init_inner_vertex(7, -kCos, -kSin);
1714
1715 const uint16_t* primIndices = circle_type_to_indices(true);
1716 const int primIndexCount = circle_type_to_index_count(true);
1717 for (int i = 0; i < primIndexCount; ++i) {
1718 *indices++ = primIndices[i] + currStartVertex;
1719 }
1720
1721 currStartVertex += circle_type_to_vert_count(true);
1722 vertices += circle_type_to_vert_count(true) * vertexStride;
1723 }
1724
1725 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04001726 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1727 GrPrimitiveRestart::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001728 mesh.setVertexData(vertexBuffer, firstVertex);
1729 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
1730 }
1731
1732 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1733 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1734
1735 // can only represent 65535 unique vertices with 16-bit indices
1736 if (fVertCount + that->fVertCount > 65536) {
1737 return false;
1738 }
1739
1740 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1741 return false;
1742 }
1743
1744 if (fHelper.usesLocalCoords() &&
1745 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1746 return false;
1747 }
1748
1749 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1750 this->joinBounds(*that);
1751 fVertCount += that->fVertCount;
1752 fIndexCount += that->fIndexCount;
1753 return true;
1754 }
1755
1756 struct Circle {
1757 GrColor fColor;
1758 SkScalar fOuterRadius;
1759 SkScalar fInnerRadius;
1760 SkScalar fOnAngle;
1761 SkScalar fTotalAngle;
1762 SkScalar fStartAngle;
1763 SkScalar fPhaseAngle;
1764 SkRect fDevBounds;
1765 };
1766
1767 SkMatrix fViewMatrixIfUsingLocalCoords;
1768 Helper fHelper;
1769 SkSTArray<1, Circle, true> fCircles;
1770 int fVertCount;
1771 int fIndexCount;
1772
1773 typedef GrMeshDrawOp INHERITED;
1774};
1775
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001776///////////////////////////////////////////////////////////////////////////////
1777
Brian Salomon05441c42017-05-15 16:45:49 -04001778class EllipseOp : public GrMeshDrawOp {
1779private:
1780 using Helper = GrSimpleMeshDrawOpHelper;
1781
1782 struct DeviceSpaceParams {
1783 SkPoint fCenter;
1784 SkScalar fXRadius;
1785 SkScalar fYRadius;
1786 SkScalar fInnerXRadius;
1787 SkScalar fInnerYRadius;
1788 };
1789
joshualitt76e7fb62015-02-11 08:52:27 -08001790public:
Brian Salomon25a88092016-12-01 09:36:50 -05001791 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001792
Brian Salomonea26d6b2018-01-23 20:33:21 +00001793 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04001794 const SkRect& ellipse, const SkStrokeRec& stroke) {
1795 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001796 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001797 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1798 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001799 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1800 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001801 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1802 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1803 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1804 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001805
bsalomon4b4a7cc2016-07-08 04:42:54 -07001806 // do (potentially) anisotropic mapping of stroke
1807 SkVector scaledStroke;
1808 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001809 scaledStroke.fX = SkScalarAbs(
1810 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1811 scaledStroke.fY = SkScalarAbs(
1812 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001813
1814 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001815 bool isStrokeOnly =
1816 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001817 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1818
Brian Salomon05441c42017-05-15 16:45:49 -04001819 params.fInnerXRadius = 0;
1820 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001821 if (hasStroke) {
1822 if (SkScalarNearlyZero(scaledStroke.length())) {
1823 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1824 } else {
1825 scaledStroke.scale(SK_ScalarHalf);
1826 }
1827
1828 // we only handle thick strokes for near-circular ellipses
1829 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001830 (0.5f * params.fXRadius > params.fYRadius ||
1831 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001832 return nullptr;
1833 }
1834
1835 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001836 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1837 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1838 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1839 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001840 return nullptr;
1841 }
1842
1843 // this is legit only if scale & translation (which should be the case at the moment)
1844 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001845 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1846 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001847 }
1848
Brian Salomon05441c42017-05-15 16:45:49 -04001849 params.fXRadius += scaledStroke.fX;
1850 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001851 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00001852 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001853 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001854
Brian Salomonea26d6b2018-01-23 20:33:21 +00001855 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1856 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1857 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001858 SkStrokeRec::Style style = stroke.getStyle();
1859 bool isStrokeOnly =
1860 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001861
Brian Salomon05441c42017-05-15 16:45:49 -04001862 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1863 params.fInnerXRadius, params.fInnerYRadius,
1864 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1865 params.fCenter.fY - params.fYRadius,
1866 params.fCenter.fX + params.fXRadius,
1867 params.fCenter.fY + params.fYRadius)});
1868
1869 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001870
bsalomon4b4a7cc2016-07-08 04:42:54 -07001871 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001872 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873
Brian Salomon05441c42017-05-15 16:45:49 -04001874 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1875 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001876 }
joshualitt76e7fb62015-02-11 08:52:27 -08001877
Brian Salomon289e3d82016-12-14 15:52:56 -05001878 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001879
Robert Phillipsf1748f52017-09-14 14:11:24 -04001880 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001881 fHelper.visitProxies(func);
1882 }
1883
Brian Salomon7c3e7182016-12-01 09:35:30 -05001884 SkString dumpInfo() const override {
1885 SkString string;
1886 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001887 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001888 string.appendf(
1889 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1890 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1891 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1892 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1893 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001894 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001895 string += fHelper.dumpInfo();
1896 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001897 return string;
1898 }
1899
Brian Osman9a725dd2017-09-20 09:53:22 -04001900 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1901 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001902 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001903 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1904 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001905 }
1906
1907 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1908
bsalomone46f9fe2015-08-18 06:05:14 -07001909private:
Brian Salomon91326c32017-08-09 16:02:19 -04001910 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001911 SkMatrix localMatrix;
1912 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001913 return;
1914 }
1915
1916 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00001917 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001918
bsalomonb5238a72015-05-05 07:49:49 -07001919 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001920 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001921 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001922 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1923 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001924 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001925 return;
1926 }
1927
Brian Salomon05441c42017-05-15 16:45:49 -04001928 for (const auto& ellipse : fEllipses) {
1929 GrColor color = ellipse.fColor;
1930 SkScalar xRadius = ellipse.fXRadius;
1931 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001932
1933 // Compute the reciprocals of the radii here to save time in the shader
1934 SkScalar xRadRecip = SkScalarInvert(xRadius);
1935 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001936 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1937 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001938
vjiaoblack977996d2016-06-30 12:20:54 -07001939 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1940 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1941 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1942
joshualitt76e7fb62015-02-11 08:52:27 -08001943 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001944 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001945 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001946 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001947 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1948 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1949
Brian Salomon05441c42017-05-15 16:45:49 -04001950 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001951 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001952 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001953 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1954 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1955
Brian Salomon57caa662017-10-18 12:21:05 +00001956 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001957 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001958 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001959 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1960 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1961
Brian Salomon57caa662017-10-18 12:21:05 +00001962 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001963 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00001964 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001965 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1966 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1967
bsalomonb5238a72015-05-05 07:49:49 -07001968 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001969 }
Brian Salomon05441c42017-05-15 16:45:49 -04001970 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001971 }
1972
Brian Salomon25a88092016-12-01 09:36:50 -05001973 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001974 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001975
Brian Salomon05441c42017-05-15 16:45:49 -04001976 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001977 return false;
1978 }
1979
bsalomoncdaa97b2016-03-08 08:30:14 -08001980 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001981 return false;
1982 }
1983
Brian Salomon05441c42017-05-15 16:45:49 -04001984 if (fHelper.usesLocalCoords() &&
1985 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001986 return false;
1987 }
1988
Brian Salomon05441c42017-05-15 16:45:49 -04001989 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001990 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001991 return true;
1992 }
1993
Brian Salomon05441c42017-05-15 16:45:49 -04001994 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001995 GrColor fColor;
1996 SkScalar fXRadius;
1997 SkScalar fYRadius;
1998 SkScalar fInnerXRadius;
1999 SkScalar fInnerYRadius;
2000 SkRect fDevBounds;
2001 };
joshualitt76e7fb62015-02-11 08:52:27 -08002002
Brian Salomon289e3d82016-12-14 15:52:56 -05002003 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002004 Helper fHelper;
2005 bool fStroked;
2006 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002007
Brian Salomon05441c42017-05-15 16:45:49 -04002008 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002009};
2010
joshualitt76e7fb62015-02-11 08:52:27 -08002011/////////////////////////////////////////////////////////////////////////////////////////////////
2012
Brian Salomon05441c42017-05-15 16:45:49 -04002013class DIEllipseOp : public GrMeshDrawOp {
2014private:
2015 using Helper = GrSimpleMeshDrawOpHelper;
2016
2017 struct DeviceSpaceParams {
2018 SkPoint fCenter;
2019 SkScalar fXRadius;
2020 SkScalar fYRadius;
2021 SkScalar fInnerXRadius;
2022 SkScalar fInnerYRadius;
2023 DIEllipseStyle fStyle;
2024 };
2025
joshualitt76e7fb62015-02-11 08:52:27 -08002026public:
Brian Salomon25a88092016-12-01 09:36:50 -05002027 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002028
Brian Salomonea26d6b2018-01-23 20:33:21 +00002029 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002030 const SkRect& ellipse, const SkStrokeRec& stroke) {
2031 DeviceSpaceParams params;
2032 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2033 params.fXRadius = SkScalarHalf(ellipse.width());
2034 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002035
bsalomon4b4a7cc2016-07-08 04:42:54 -07002036 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002037 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2038 ? DIEllipseStyle::kStroke
2039 : (SkStrokeRec::kHairline_Style == style)
2040 ? DIEllipseStyle::kHairline
2041 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002042
Brian Salomon05441c42017-05-15 16:45:49 -04002043 params.fInnerXRadius = 0;
2044 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002045 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2046 SkScalar strokeWidth = stroke.getWidth();
2047
2048 if (SkScalarNearlyZero(strokeWidth)) {
2049 strokeWidth = SK_ScalarHalf;
2050 } else {
2051 strokeWidth *= SK_ScalarHalf;
2052 }
2053
2054 // we only handle thick strokes for near-circular ellipses
2055 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002056 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2057 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002058 return nullptr;
2059 }
2060
2061 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002062 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2063 (strokeWidth * strokeWidth) * params.fXRadius) {
2064 return nullptr;
2065 }
2066 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2067 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002068 return nullptr;
2069 }
2070
2071 // set inner radius (if needed)
2072 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002073 params.fInnerXRadius = params.fXRadius - strokeWidth;
2074 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002075 }
2076
Brian Salomon05441c42017-05-15 16:45:49 -04002077 params.fXRadius += strokeWidth;
2078 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002079 }
Brian Salomon05441c42017-05-15 16:45:49 -04002080 if (DIEllipseStyle::kStroke == params.fStyle &&
2081 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2082 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002083 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002084 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002085 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002086
Brian Salomonea26d6b2018-01-23 20:33:21 +00002087 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
2088 const SkMatrix& viewMatrix)
2089 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002090 // This expands the outer rect so that after CTM we end up with a half-pixel border
2091 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2092 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2093 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2094 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05002095 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2096 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002097
Brian Salomon05441c42017-05-15 16:45:49 -04002098 fEllipses.emplace_back(
2099 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2100 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2101 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2102 params.fCenter.fY - params.fYRadius - geoDy,
2103 params.fCenter.fX + params.fXRadius + geoDx,
2104 params.fCenter.fY + params.fYRadius + geoDy)});
2105 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2106 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002107 }
2108
Brian Salomon289e3d82016-12-14 15:52:56 -05002109 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002110
Robert Phillipsf1748f52017-09-14 14:11:24 -04002111 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002112 fHelper.visitProxies(func);
2113 }
2114
Brian Salomon7c3e7182016-12-01 09:35:30 -05002115 SkString dumpInfo() const override {
2116 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002117 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002118 string.appendf(
2119 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2120 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2121 "GeoDY: %.2f\n",
2122 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
2123 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2124 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002125 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002126 string += fHelper.dumpInfo();
2127 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002128 return string;
2129 }
2130
Brian Osman9a725dd2017-09-20 09:53:22 -04002131 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2132 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002133 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002134 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2135 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002136 }
2137
2138 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2139
bsalomone46f9fe2015-08-18 06:05:14 -07002140private:
Brian Salomon91326c32017-08-09 16:02:19 -04002141 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002142 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002143 sk_sp<GrGeometryProcessor> gp(
Brian Salomonea26d6b2018-01-23 20:33:21 +00002144 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002145
joshualitt76e7fb62015-02-11 08:52:27 -08002146 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08002147 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07002148 QuadHelper helper;
2149 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04002150 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002151 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002152 return;
2153 }
2154
Brian Salomon05441c42017-05-15 16:45:49 -04002155 for (const auto& ellipse : fEllipses) {
2156 GrColor color = ellipse.fColor;
2157 SkScalar xRadius = ellipse.fXRadius;
2158 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002159
Brian Salomon05441c42017-05-15 16:45:49 -04002160 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002161
2162 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002163 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2164 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002165
joshualitt76e7fb62015-02-11 08:52:27 -08002166 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002167 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002168 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002169 verts[0].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002170
Brian Salomon289e3d82016-12-14 15:52:56 -05002171 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002172 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002173 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002174 verts[1].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002175
Brian Salomon57caa662017-10-18 12:21:05 +00002176 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002177 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002178 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002179 verts[2].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002180
Brian Salomon57caa662017-10-18 12:21:05 +00002181 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002182 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002183 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002184 verts[3].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
2185
2186 if (DIEllipseStyle::kStroke == this->style()) {
2187 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
2188 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
2189
2190 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2191 -innerRatioY - offsetDy);
2192 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2193 innerRatioY + offsetDy);
2194 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2195 -innerRatioY - offsetDy);
2196 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2197 innerRatioY + offsetDy);
2198 }
joshualitt76e7fb62015-02-11 08:52:27 -08002199
bsalomonb5238a72015-05-05 07:49:49 -07002200 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002201 }
Brian Salomon05441c42017-05-15 16:45:49 -04002202 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002203 }
halcanary9d524f22016-03-29 09:03:52 -07002204
Brian Salomon25a88092016-12-01 09:36:50 -05002205 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002206 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002207 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002208 return false;
2209 }
2210
bsalomoncdaa97b2016-03-08 08:30:14 -08002211 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08002212 return false;
2213 }
2214
joshualittd96a67b2015-05-05 14:09:05 -07002215 // TODO rewrite to allow positioning on CPU
2216 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08002217 return false;
2218 }
2219
Brian Salomon05441c42017-05-15 16:45:49 -04002220 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002221 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002222 return true;
2223 }
2224
Brian Salomon05441c42017-05-15 16:45:49 -04002225 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2226 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002227
Brian Salomon05441c42017-05-15 16:45:49 -04002228 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002229 SkMatrix fViewMatrix;
2230 GrColor fColor;
2231 SkScalar fXRadius;
2232 SkScalar fYRadius;
2233 SkScalar fInnerXRadius;
2234 SkScalar fInnerYRadius;
2235 SkScalar fGeoDx;
2236 SkScalar fGeoDy;
2237 DIEllipseStyle fStyle;
2238 SkRect fBounds;
2239 };
2240
Brian Salomon05441c42017-05-15 16:45:49 -04002241 Helper fHelper;
2242 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002243
Brian Salomon05441c42017-05-15 16:45:49 -04002244 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002245};
2246
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002247///////////////////////////////////////////////////////////////////////////////
2248
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002249// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002250//
2251// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2252// ____________
2253// |_|________|_|
2254// | | | |
2255// | | | |
2256// | | | |
2257// |_|________|_|
2258// |_|________|_|
2259//
2260// For strokes, we don't draw the center quad.
2261//
2262// For circular roundrects, in the case where the stroke width is greater than twice
2263// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002264// in the center. The shared vertices are duplicated so we can set a different outer radius
2265// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002266// ____________
2267// |_|________|_|
2268// | |\ ____ /| |
2269// | | | | | |
2270// | | |____| | |
2271// |_|/______\|_|
2272// |_|________|_|
2273//
2274// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002275//
2276// For filled rrects that need to provide a distance vector we resuse the overstroke
2277// geometry but make the inner rect degenerate (either a point or a horizontal or
2278// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002279
jvanverth84839f62016-08-29 10:16:40 -07002280static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002281 // clang-format off
2282 // overstroke quads
2283 // we place this at the beginning so that we can skip these indices when rendering normally
2284 16, 17, 19, 16, 19, 18,
2285 19, 17, 23, 19, 23, 21,
2286 21, 23, 22, 21, 22, 20,
2287 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002288
Brian Salomon289e3d82016-12-14 15:52:56 -05002289 // corners
2290 0, 1, 5, 0, 5, 4,
2291 2, 3, 7, 2, 7, 6,
2292 8, 9, 13, 8, 13, 12,
2293 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002294
Brian Salomon289e3d82016-12-14 15:52:56 -05002295 // edges
2296 1, 2, 6, 1, 6, 5,
2297 4, 5, 9, 4, 9, 8,
2298 6, 7, 11, 6, 11, 10,
2299 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002300
Brian Salomon289e3d82016-12-14 15:52:56 -05002301 // center
2302 // we place this at the end so that we can ignore these indices when not rendering as filled
2303 5, 6, 10, 5, 10, 9,
2304 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305};
Brian Salomon289e3d82016-12-14 15:52:56 -05002306
jvanverth84839f62016-08-29 10:16:40 -07002307// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002308static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002309
jvanverth84839f62016-08-29 10:16:40 -07002310// overstroke count is arraysize minus the center indices
2311static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2312// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002313static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002314// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002315static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2316static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002317static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002318
jvanverthc3d0e422016-08-25 08:12:35 -07002319enum RRectType {
2320 kFill_RRectType,
2321 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002322 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002323};
2324
jvanverth84839f62016-08-29 10:16:40 -07002325static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002326 switch (type) {
2327 case kFill_RRectType:
2328 case kStroke_RRectType:
2329 return kVertsPerStandardRRect;
2330 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002331 return kVertsPerOverstrokeRRect;
2332 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002333 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002334 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002335}
2336
2337static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002338 switch (type) {
2339 case kFill_RRectType:
2340 return kIndicesPerFillRRect;
2341 case kStroke_RRectType:
2342 return kIndicesPerStrokeRRect;
2343 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002344 return kIndicesPerOverstrokeRRect;
2345 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002346 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002347 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002348}
2349
2350static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002351 switch (type) {
2352 case kFill_RRectType:
2353 case kStroke_RRectType:
2354 return gStandardRRectIndices;
2355 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002356 return gOverstrokeRRectIndices;
2357 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002358 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002359 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002360}
2361
joshualitt76e7fb62015-02-11 08:52:27 -08002362///////////////////////////////////////////////////////////////////////////////////////////////////
2363
Robert Phillips79839d42016-10-06 15:03:34 -04002364// For distance computations in the interior of filled rrects we:
2365//
2366// add a interior degenerate (point or line) rect
2367// each vertex of that rect gets -outerRad as its radius
2368// this makes the computation of the distance to the outer edge be negative
2369// negative values are caught and then handled differently in the GP's onEmitCode
2370// each vertex is also given the normalized x & y distance from the interior rect's edge
2371// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2372
Brian Salomon05441c42017-05-15 16:45:49 -04002373class CircularRRectOp : public GrMeshDrawOp {
2374private:
2375 using Helper = GrSimpleMeshDrawOpHelper;
2376
joshualitt76e7fb62015-02-11 08:52:27 -08002377public:
Brian Salomon25a88092016-12-01 09:36:50 -05002378 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002379
bsalomon4b4a7cc2016-07-08 04:42:54 -07002380 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2381 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002382 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomone23bffd2017-06-02 11:01:10 -04002383 const SkRect& devRect, float devRadius,
2384 float devStrokeWidth, bool strokeOnly) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00002385 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomone23bffd2017-06-02 11:01:10 -04002386 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002387 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002388 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
2389 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002390 : INHERITED(ClassID())
2391 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002392 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002393 SkRect bounds = devRect;
2394 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2395 SkScalar innerRadius = 0.0f;
2396 SkScalar outerRadius = devRadius;
2397 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002398 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002399 if (devStrokeWidth > 0) {
2400 if (SkScalarNearlyZero(devStrokeWidth)) {
2401 halfWidth = SK_ScalarHalf;
2402 } else {
2403 halfWidth = SkScalarHalf(devStrokeWidth);
2404 }
joshualitt76e7fb62015-02-11 08:52:27 -08002405
bsalomon4b4a7cc2016-07-08 04:42:54 -07002406 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002407 // Outset stroke by 1/4 pixel
2408 devStrokeWidth += 0.25f;
2409 // If stroke is greater than width or height, this is still a fill
2410 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002411 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002412 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002413 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002414 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002415 }
2416 outerRadius += halfWidth;
2417 bounds.outset(halfWidth, halfWidth);
2418 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002419
bsalomon4b4a7cc2016-07-08 04:42:54 -07002420 // The radii are outset for two reasons. First, it allows the shader to simply perform
2421 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2422 // Second, the outer radius is used to compute the verts of the bounding box that is
2423 // rendered and the outset ensures the box will cover all partially covered by the rrect
2424 // corners.
2425 outerRadius += SK_ScalarHalf;
2426 innerRadius -= SK_ScalarHalf;
2427
bsalomon88cf17d2016-07-08 06:40:56 -07002428 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2429
2430 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002431 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2432
Brian Salomon05441c42017-05-15 16:45:49 -04002433 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002434 fVertCount = rrect_type_to_vert_count(type);
2435 fIndexCount = rrect_type_to_index_count(type);
2436 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002437 }
2438
Brian Salomon289e3d82016-12-14 15:52:56 -05002439 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002440
Robert Phillipsf1748f52017-09-14 14:11:24 -04002441 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002442 fHelper.visitProxies(func);
2443 }
2444
jvanverthc3d0e422016-08-25 08:12:35 -07002445 SkString dumpInfo() const override {
2446 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002447 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002448 string.appendf(
2449 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2450 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04002451 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
2452 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
2453 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002454 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002455 string += fHelper.dumpInfo();
2456 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002457 return string;
2458 }
2459
Brian Osman9a725dd2017-09-20 09:53:22 -04002460 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2461 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002462 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002463 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2464 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002465 }
2466
2467 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2468
Brian Salomon92aee3d2016-12-21 09:20:25 -05002469private:
Robert Phillips79839d42016-10-06 15:03:34 -04002470 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05002471 SkPoint fPos;
2472 GrColor fColor;
2473 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04002474 SkScalar fOuterRadius;
2475 SkScalar fInnerRadius;
2476 // No half plane, we don't use it here.
2477 };
2478
Brian Salomon289e3d82016-12-14 15:52:56 -05002479 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
2480 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2481 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002482 SkASSERT(smInset < bigInset);
2483
2484 // TL
2485 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
2486 (*verts)->fColor = color;
2487 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2488 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002489 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002490 (*verts)++;
2491
2492 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05002493 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002494 (*verts)->fColor = color;
2495 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2496 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002497 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002498 (*verts)++;
2499
Brian Salomon289e3d82016-12-14 15:52:56 -05002500 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002501 (*verts)->fColor = color;
2502 (*verts)->fOffset = SkPoint::Make(0, 0);
2503 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002504 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002505 (*verts)++;
2506
Brian Salomon289e3d82016-12-14 15:52:56 -05002507 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002508 (*verts)->fColor = color;
2509 (*verts)->fOffset = SkPoint::Make(0, 0);
2510 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002511 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002512 (*verts)++;
2513
2514 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
2515 (*verts)->fColor = color;
2516 (*verts)->fOffset = SkPoint::Make(0, 0);
2517 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002518 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002519 (*verts)++;
2520
2521 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
2522 (*verts)->fColor = color;
2523 (*verts)->fOffset = SkPoint::Make(0, 0);
2524 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002525 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002526 (*verts)++;
2527
2528 // BL
2529 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
2530 (*verts)->fColor = color;
2531 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2532 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002533 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002534 (*verts)++;
2535
2536 // BR
2537 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
2538 (*verts)->fColor = color;
2539 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2540 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002541 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002542 (*verts)++;
2543 }
2544
Brian Salomon91326c32017-08-09 16:02:19 -04002545 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002546 // Invert the view matrix as a local matrix (if any other processors require coords).
2547 SkMatrix localMatrix;
2548 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002549 return;
2550 }
2551
2552 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002553 sk_sp<GrGeometryProcessor> gp(
Brian Salomon45c92202018-04-10 10:53:58 -04002554 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002555
joshualitt76e7fb62015-02-11 08:52:27 -08002556 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07002557 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08002558
jvanverth84839f62016-08-29 10:16:40 -07002559 const GrBuffer* vertexBuffer;
2560 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002561
Brian Salomon289e3d82016-12-14 15:52:56 -05002562 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
2563 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07002564 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002565 SkDebugf("Could not allocate vertices\n");
2566 return;
2567 }
2568
jvanverth84839f62016-08-29 10:16:40 -07002569 const GrBuffer* indexBuffer = nullptr;
2570 int firstIndex = 0;
2571 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2572 if (!indices) {
2573 SkDebugf("Could not allocate indices\n");
2574 return;
2575 }
2576
2577 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002578 for (const auto& rrect : fRRects) {
2579 GrColor color = rrect.fColor;
2580 SkScalar outerRadius = rrect.fOuterRadius;
2581 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002582
Brian Salomon289e3d82016-12-14 15:52:56 -05002583 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2584 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002585
Brian Salomon289e3d82016-12-14 15:52:56 -05002586 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002587 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002588 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002589 SkScalar innerRadius = rrect.fType != kFill_RRectType
2590 ? rrect.fInnerRadius / rrect.fOuterRadius
2591 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002592 for (int i = 0; i < 4; ++i) {
2593 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002594 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002595 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
2596 verts->fOuterRadius = outerRadius;
2597 verts->fInnerRadius = innerRadius;
2598 verts++;
2599
2600 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002601 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002602 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2603 verts->fOuterRadius = outerRadius;
2604 verts->fInnerRadius = innerRadius;
2605 verts++;
2606
2607 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002608 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002609 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2610 verts->fOuterRadius = outerRadius;
2611 verts->fInnerRadius = innerRadius;
2612 verts++;
2613
2614 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002615 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002616 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
2617 verts->fOuterRadius = outerRadius;
2618 verts->fInnerRadius = innerRadius;
2619 verts++;
2620 }
jvanverthc3d0e422016-08-25 08:12:35 -07002621 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002622 // Effectively this is an additional stroked rrect, with its
2623 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2624 // This will give us correct AA in the center and the correct
2625 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002626 //
jvanvertha4f1af82016-08-29 07:17:47 -07002627 // Also, the outer offset is a constant vector pointing to the right, which
2628 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002629 if (kOverstroke_RRectType == rrect.fType) {
2630 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002631
Brian Salomon05441c42017-05-15 16:45:49 -04002632 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002633 // this is the normalized distance from the outer rectangle of this
2634 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002635 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002636
Brian Salomon289e3d82016-12-14 15:52:56 -05002637 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002638 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002639 }
jvanverth6a397612016-08-26 08:15:33 -07002640
Brian Salomon05441c42017-05-15 16:45:49 -04002641 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2642 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002643 for (int i = 0; i < primIndexCount; ++i) {
2644 *indices++ = primIndices[i] + currStartVertex;
2645 }
2646
Brian Salomon05441c42017-05-15 16:45:49 -04002647 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002648 }
2649
Chris Dalton3809bab2017-06-13 10:55:06 -06002650 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04002651 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
2652 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06002653 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04002654 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002655 }
2656
Brian Salomon25a88092016-12-01 09:36:50 -05002657 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002658 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002659
2660 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002661 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002662 return false;
2663 }
2664
Brian Salomon05441c42017-05-15 16:45:49 -04002665 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002666 return false;
2667 }
2668
Brian Salomon05441c42017-05-15 16:45:49 -04002669 if (fHelper.usesLocalCoords() &&
2670 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002671 return false;
2672 }
2673
Brian Salomon05441c42017-05-15 16:45:49 -04002674 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002675 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002676 fVertCount += that->fVertCount;
2677 fIndexCount += that->fIndexCount;
2678 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002679 return true;
2680 }
2681
Brian Salomon05441c42017-05-15 16:45:49 -04002682 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002683 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002684 SkScalar fInnerRadius;
2685 SkScalar fOuterRadius;
2686 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002687 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002688 };
2689
Brian Salomon289e3d82016-12-14 15:52:56 -05002690 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002691 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002692 int fVertCount;
2693 int fIndexCount;
2694 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002695 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002696
Brian Salomon05441c42017-05-15 16:45:49 -04002697 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002698};
2699
jvanverth84839f62016-08-29 10:16:40 -07002700static const int kNumRRectsInIndexBuffer = 256;
2701
2702GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2703GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002704static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2705 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002706 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2707 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2708 switch (type) {
2709 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002710 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002711 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2712 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002713 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002714 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002715 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2716 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002717 default:
2718 SkASSERT(false);
2719 return nullptr;
2720 };
2721}
2722
Brian Salomon05441c42017-05-15 16:45:49 -04002723class EllipticalRRectOp : public GrMeshDrawOp {
2724private:
2725 using Helper = GrSimpleMeshDrawOpHelper;
2726
joshualitt76e7fb62015-02-11 08:52:27 -08002727public:
Brian Salomon25a88092016-12-01 09:36:50 -05002728 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002729
bsalomon4b4a7cc2016-07-08 04:42:54 -07002730 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2731 // whether the rrect is only stroked or stroked and filled.
Brian Salomonea26d6b2018-01-23 20:33:21 +00002732 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -04002733 const SkRect& devRect, float devXRadius, float devYRadius,
2734 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002735 SkASSERT(devXRadius > 0.5);
2736 SkASSERT(devYRadius > 0.5);
2737 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2738 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002739 if (devStrokeWidths.fX > 0) {
2740 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2741 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2742 } else {
2743 devStrokeWidths.scale(SK_ScalarHalf);
2744 }
joshualitt76e7fb62015-02-11 08:52:27 -08002745
bsalomon4b4a7cc2016-07-08 04:42:54 -07002746 // we only handle thick strokes for near-circular ellipses
2747 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002748 (SK_ScalarHalf * devXRadius > devYRadius ||
2749 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002750 return nullptr;
2751 }
2752
2753 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002754 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2755 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002756 return nullptr;
2757 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002758 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2759 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002760 return nullptr;
2761 }
Brian Salomon05441c42017-05-15 16:45:49 -04002762 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002763 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002764 devXRadius, devYRadius, devStrokeWidths,
2765 strokeOnly);
2766 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002767
Brian Salomonea26d6b2018-01-23 20:33:21 +00002768 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2769 const SkRect& devRect, float devXRadius, float devYRadius,
2770 SkVector devStrokeHalfWidths, bool strokeOnly)
2771 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002772 SkScalar innerXRadius = 0.0f;
2773 SkScalar innerYRadius = 0.0f;
2774 SkRect bounds = devRect;
2775 bool stroked = false;
2776 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002777 // this is legit only if scale & translation (which should be the case at the moment)
2778 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002779 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2780 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002781 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2782 }
2783
Brian Salomon05441c42017-05-15 16:45:49 -04002784 devXRadius += devStrokeHalfWidths.fX;
2785 devYRadius += devStrokeHalfWidths.fY;
2786 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002787 }
2788
Brian Salomon05441c42017-05-15 16:45:49 -04002789 fStroked = stroked;
2790 fViewMatrixIfUsingLocalCoords = viewMatrix;
2791 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002792 // Expand the rect for aa in order to generate the correct vertices.
2793 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002794 fRRects.emplace_back(
2795 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002796 }
2797
Brian Salomon289e3d82016-12-14 15:52:56 -05002798 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002799
Robert Phillipsf1748f52017-09-14 14:11:24 -04002800 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002801 fHelper.visitProxies(func);
2802 }
2803
Brian Salomon7c3e7182016-12-01 09:35:30 -05002804 SkString dumpInfo() const override {
2805 SkString string;
2806 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002807 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002808 string.appendf(
2809 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2810 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2811 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2812 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2813 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002814 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002815 string += fHelper.dumpInfo();
2816 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002817 return string;
2818 }
2819
Brian Osman9a725dd2017-09-20 09:53:22 -04002820 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2821 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002822 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002823 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2824 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002825 }
2826
2827 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2828
bsalomone46f9fe2015-08-18 06:05:14 -07002829private:
Brian Salomon91326c32017-08-09 16:02:19 -04002830 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002831 SkMatrix localMatrix;
2832 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002833 return;
2834 }
2835
2836 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00002837 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002838
joshualitt76e7fb62015-02-11 08:52:27 -08002839 size_t vertexStride = gp->getVertexStride();
2840 SkASSERT(vertexStride == sizeof(EllipseVertex));
2841
bsalomonb5238a72015-05-05 07:49:49 -07002842 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002843 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002844 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2845 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002846
Chris Dalton3809bab2017-06-13 10:55:06 -06002847 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002848 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002849 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002850 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002851 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002852 SkDebugf("Could not allocate vertices\n");
2853 return;
2854 }
2855
Brian Salomon05441c42017-05-15 16:45:49 -04002856 for (const auto& rrect : fRRects) {
2857 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002858 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002859 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2860 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2861 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2862 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002863
2864 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002865 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2866 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002867
Brian Salomon05441c42017-05-15 16:45:49 -04002868 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002869
Brian Salomon289e3d82016-12-14 15:52:56 -05002870 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2871 bounds.fBottom - yOuterRadius, bounds.fBottom};
2872 SkScalar yOuterOffsets[4] = {yOuterRadius,
2873 SK_ScalarNearlyZero, // we're using inversesqrt() in
2874 // shader, so can't be exactly 0
2875 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002876
2877 for (int i = 0; i < 4; ++i) {
2878 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002879 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002880 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2881 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2882 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2883 verts++;
2884
2885 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002886 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002887 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2888 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2889 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2890 verts++;
2891
2892 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002893 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002894 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2895 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2896 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2897 verts++;
2898
2899 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002900 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002901 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2902 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2903 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2904 verts++;
2905 }
2906 }
Brian Salomon05441c42017-05-15 16:45:49 -04002907 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002908 }
2909
Brian Salomon25a88092016-12-01 09:36:50 -05002910 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002911 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002912
Brian Salomon05441c42017-05-15 16:45:49 -04002913 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002914 return false;
2915 }
2916
bsalomoncdaa97b2016-03-08 08:30:14 -08002917 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002918 return false;
2919 }
2920
Brian Salomon05441c42017-05-15 16:45:49 -04002921 if (fHelper.usesLocalCoords() &&
2922 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002923 return false;
2924 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002925
Brian Salomon05441c42017-05-15 16:45:49 -04002926 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002927 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002928 return true;
2929 }
2930
Brian Salomon05441c42017-05-15 16:45:49 -04002931 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002932 GrColor fColor;
2933 SkScalar fXRadius;
2934 SkScalar fYRadius;
2935 SkScalar fInnerXRadius;
2936 SkScalar fInnerYRadius;
2937 SkRect fDevBounds;
2938 };
2939
Brian Salomon289e3d82016-12-14 15:52:56 -05002940 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002941 Helper fHelper;
2942 bool fStroked;
2943 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002944
Brian Salomon05441c42017-05-15 16:45:49 -04002945 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002946};
2947
Brian Salomon05441c42017-05-15 16:45:49 -04002948static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002949 const SkMatrix& viewMatrix,
2950 const SkRRect& rrect,
2951 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002952 SkASSERT(viewMatrix.rectStaysRect());
2953 SkASSERT(rrect.isSimple());
2954 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002955
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002956 // RRect ops only handle simple, but not too simple, rrects.
2957 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002958 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002959 SkRect bounds;
2960 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002961
Mike Reed242135a2018-02-22 13:41:39 -05002962 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05002963 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2964 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2965 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2966 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002967
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002968 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002969
bsalomon4b4a7cc2016-07-08 04:42:54 -07002970 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2971 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002972 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002973
Brian Salomon289e3d82016-12-14 15:52:56 -05002974 bool isStrokeOnly =
2975 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002976 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2977
jvanverthc3d0e422016-08-25 08:12:35 -07002978 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002979 if (hasStroke) {
2980 if (SkStrokeRec::kHairline_Style == style) {
2981 scaledStroke.set(1, 1);
2982 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002983 scaledStroke.fX = SkScalarAbs(
2984 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2985 scaledStroke.fY = SkScalarAbs(
2986 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002987 }
2988
jvanverthc3d0e422016-08-25 08:12:35 -07002989 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2990 // for non-circular rrects, if half of strokewidth is greater than radius,
2991 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002992 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2993 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002994 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002995 }
2996 }
2997
2998 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2999 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3000 // patch will have fractional coverage. This only matters when the interior is actually filled.
3001 // We could consider falling back to rect rendering here, since a tiny radius is
3002 // indistinguishable from a square corner.
3003 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003004 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003005 }
3006
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003007 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003008 if (isCircular) {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003009 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
3010 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003011 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003012 } else {
Brian Salomonea26d6b2018-01-23 20:33:21 +00003013 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
Brian Salomon05441c42017-05-15 16:45:49 -04003014 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003015 }
joshualitt3e708c52015-04-30 13:49:27 -07003016}
3017
Brian Salomonea26d6b2018-01-23 20:33:21 +00003018std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
3019 const SkMatrix& viewMatrix,
3020 const SkRRect& rrect,
3021 const SkStrokeRec& stroke,
3022 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003023 if (rrect.isOval()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003024 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), GrStyle(stroke, nullptr),
3025 shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003026 }
3027
3028 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003029 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003030 }
3031
Brian Salomonea26d6b2018-01-23 20:33:21 +00003032 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003033}
joshualitt3e708c52015-04-30 13:49:27 -07003034
bsalomon4b4a7cc2016-07-08 04:42:54 -07003035///////////////////////////////////////////////////////////////////////////////
3036
Brian Salomonea26d6b2018-01-23 20:33:21 +00003037std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
3038 const SkMatrix& viewMatrix,
3039 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003040 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003041 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003042 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003043 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003044 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3045 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003046 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003047 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003048 if (style.hasNonDashPathEffect()) {
3049 return nullptr;
3050 } else if (style.isDashed()) {
3051 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3052 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3053 return nullptr;
3054 }
3055 auto onInterval = style.dashIntervals()[0];
3056 auto offInterval = style.dashIntervals()[1];
3057 if (offInterval == 0) {
3058 GrStyle strokeStyle(style.strokeRec(), nullptr);
3059 return MakeOvalOp(std::move(paint), viewMatrix, oval, strokeStyle, shaderCaps);
3060 } else if (onInterval == 0) {
3061 // There is nothing to draw but we have no way to indicate that here.
3062 return nullptr;
3063 }
3064 auto angularOnInterval = onInterval / r;
3065 auto angularOffInterval = offInterval / r;
3066 auto phaseAngle = style.dashPhase() / r;
3067 // Currently this function doesn't accept ovals with different start angles, though
3068 // it could.
3069 static const SkScalar kStartAngle = 0.f;
3070 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, r,
3071 style.strokeRec().getWidth(), kStartAngle,
3072 angularOnInterval, angularOffInterval, phaseAngle);
3073 }
3074 return CircleOp::Make(std::move(paint), viewMatrix, center, r, style);
3075 }
3076
3077 if (style.pathEffect()) {
3078 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003079 }
3080
Stan Ilieveb868aa2017-02-21 11:06:16 -05003081 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003082 if (viewMatrix.rectStaysRect()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003083 return EllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003084 }
3085
Stan Ilieveb868aa2017-02-21 11:06:16 -05003086 // Otherwise, if we have shader derivative support, render as device-independent
3087 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003088 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec());
Stan Ilieveb868aa2017-02-21 11:06:16 -05003089 }
3090
bsalomon4b4a7cc2016-07-08 04:42:54 -07003091 return nullptr;
3092}
3093
3094///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003095
Brian Salomonea26d6b2018-01-23 20:33:21 +00003096std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
3097 const SkRect& oval, SkScalar startAngle,
3098 SkScalar sweepAngle, bool useCenter,
3099 const GrStyle& style,
3100 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003101 SkASSERT(!oval.isEmpty());
3102 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003103 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003104 if (SkScalarAbs(sweepAngle) >= 360.f) {
3105 return nullptr;
3106 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003107 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3108 return nullptr;
3109 }
3110 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003111 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3112 useCenter};
Brian Salomonea26d6b2018-01-23 20:33:21 +00003113 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003114}
3115
3116///////////////////////////////////////////////////////////////////////////////
3117
Hal Canary6f6961e2017-01-31 13:50:44 -05003118#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003119
Brian Salomon05441c42017-05-15 16:45:49 -04003120GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003121 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003122 SkScalar rotate = random->nextSScalar1() * 360.f;
3123 SkScalar translateX = random->nextSScalar1() * 1000.f;
3124 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003125 SkScalar scale;
3126 do {
3127 scale = random->nextSScalar1() * 100.f;
3128 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003129 SkMatrix viewMatrix;
3130 viewMatrix.setRotate(rotate);
3131 viewMatrix.postTranslate(translateX, translateY);
3132 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003133 SkRect circle = GrTest::TestSquare(random);
3134 SkPoint center = {circle.centerX(), circle.centerY()};
3135 SkScalar radius = circle.width() / 2.f;
3136 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003137 CircleOp::ArcParams arcParamsTmp;
3138 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003139 if (random->nextBool()) {
3140 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003141 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3142 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003143 arcParams = &arcParamsTmp;
3144 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003145 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
3146 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003147 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003148 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003149 }
3150 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003151}
3152
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003153GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3154 SkScalar rotate = random->nextSScalar1() * 360.f;
3155 SkScalar translateX = random->nextSScalar1() * 1000.f;
3156 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003157 SkScalar scale;
3158 do {
3159 scale = random->nextSScalar1() * 100.f;
3160 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003161 SkMatrix viewMatrix;
3162 viewMatrix.setRotate(rotate);
3163 viewMatrix.postTranslate(translateX, translateY);
3164 viewMatrix.postScale(scale, scale);
3165 SkRect circle = GrTest::TestSquare(random);
3166 SkPoint center = {circle.centerX(), circle.centerY()};
3167 SkScalar radius = circle.width() / 2.f;
3168 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3169 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3170 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3171 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3172 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
3173 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, radius, strokeWidth,
3174 startAngle, onAngle, offAngle, phase);
3175}
3176
Brian Salomon05441c42017-05-15 16:45:49 -04003177GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003178 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003179 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003180 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003181}
3182
Brian Salomon05441c42017-05-15 16:45:49 -04003183GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003184 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003185 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003186 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003187}
3188
Brian Salomon05441c42017-05-15 16:45:49 -04003189GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003190 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003191 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomonea26d6b2018-01-23 20:33:21 +00003192 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003193}
3194
3195#endif