blob: ebc602d3fd944629baa9046a3250137c4a231973 [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
Ben Wagnerf08d1d02018-06-18 15:11:00 -040028#include <utility>
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050033 SkPoint fPos;
34 GrColor fColor;
35 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050041 SkPoint fPos;
42 GrColor fColor;
43 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
Brian Salomon289e3d82016-12-14 15:52:56 -050047static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000048}
49
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000050///////////////////////////////////////////////////////////////////////////////
51
52/**
bsalomonce1c8862014-12-15 07:11:22 -080053 * The output of this effect is a modulation of the input color and coverage for a circle. It
54 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080057 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080058 * vec4f : (p.xy, outerRad, innerRad)
59 * p is the position in the normalized space.
60 * outerRad is the outerRadius in device space.
61 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070062 * Additional clip planes are supported for rendering circular arcs. The additional planes are
63 * either intersected or unioned together. Up to three planes are supported (an initial plane,
64 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050065 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070066 * types of arcs.
Brian Salomon45c92202018-04-10 10:53:58 -040067 * Round caps for stroking are allowed as well. The caps are specified as two circle center points
68 * in the same space as p.xy.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000069 */
70
bsalomoncdaa97b2016-03-08 08:30:14 -080071class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072public:
Brian Salomonea26d6b2018-01-23 20:33:21 +000073 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
Brian Salomon45c92202018-04-10 10:53:58 -040074 bool roundCaps, const SkMatrix& localMatrix)
Ethan Nicholasabff9562017-10-09 10:54:08 -040075 : INHERITED(kCircleGeometryProcessor_ClassID)
Brian Salomon45c92202018-04-10 10:53:58 -040076 , fLocalMatrix(localMatrix)
77 , fStroke(stroke) {
Brian Salomon92be2f72018-06-19 14:33:47 -040078 int cnt = 3;
bsalomon4f3a0ca2016-08-22 13:14:26 -070079 if (clipPlane) {
Brian Salomon92be2f72018-06-19 14:33:47 -040080 fInClipPlane = {"inClipPlane", kHalf3_GrVertexAttribType};
81 ++cnt;
bsalomon4f3a0ca2016-08-22 13:14:26 -070082 }
83 if (isectPlane) {
Brian Salomon92be2f72018-06-19 14:33:47 -040084 fInIsectPlane = {"inIsectPlane", kHalf3_GrVertexAttribType};
85 ++cnt;
bsalomon4f3a0ca2016-08-22 13:14:26 -070086 }
87 if (unionPlane) {
Brian Salomon92be2f72018-06-19 14:33:47 -040088 fInUnionPlane = {"inUnionPlane", kHalf3_GrVertexAttribType};
89 ++cnt;
bsalomon4f3a0ca2016-08-22 13:14:26 -070090 }
Brian Salomon45c92202018-04-10 10:53:58 -040091 if (roundCaps) {
92 SkASSERT(stroke);
93 SkASSERT(clipPlane);
Brian Salomon92be2f72018-06-19 14:33:47 -040094 fInRoundCapCenters = {"inRoundCapCenters", kFloat4_GrVertexAttribType};
95 ++cnt;
Brian Salomon45c92202018-04-10 10:53:58 -040096 }
Brian Salomon92be2f72018-06-19 14:33:47 -040097 this->setVertexAttributeCnt(cnt);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000098 }
99
Brian Salomond3b65972017-03-22 12:05:03 -0400100 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000101
mtklein36352bf2015-03-25 18:17:31 -0700102 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000103
Brian Salomon94efbf52016-11-29 13:43:05 -0500104 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700105 GLSLProcessor::GenKey(*this, caps, b);
106 }
107
Brian Salomon94efbf52016-11-29 13:43:05 -0500108 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700109 return new GLSLProcessor();
110 }
111
112private:
egdaniel57d3b032015-11-13 11:57:27 -0800113 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000114 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800115 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000116
Brian Salomon289e3d82016-12-14 15:52:56 -0500117 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800118 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800119 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800120 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800121 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Chris Dalton60283612018-02-14 13:38:14 -0700122 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800123
joshualittabb52a12015-01-13 15:02:10 -0800124 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800125 varyingHandler->emitAttributes(cgp);
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400126 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400127 varyingHandler->addPassThroughAttribute(cgp.kInCircleEdge, "circleEdge");
128 if (cgp.fInClipPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400129 fragBuilder->codeAppend("half3 clipPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
131 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400132 if (cgp.fInIsectPlane.isInitialized()) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400133 fragBuilder->codeAppend("half3 isectPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
135 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400136 if (cgp.fInUnionPlane.isInitialized()) {
137 SkASSERT(cgp.fInClipPlane.isInitialized());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400138 fragBuilder->codeAppend("half3 unionPlane;");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700139 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
140 }
Brian Salomon45c92202018-04-10 10:53:58 -0400141 GrGLSLVarying capRadius(kFloat_GrSLType);
Brian Salomon92be2f72018-06-19 14:33:47 -0400142 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon45c92202018-04-10 10:53:58 -0400143 fragBuilder->codeAppend("float4 roundCapCenters;");
144 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
145 varyingHandler->addVarying("capRadius", &capRadius,
146 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
147 // This is the cap radius in normalized space where the outer radius is 1 and
148 // circledEdge.w is the normalized inner radius.
149 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
Brian Salomon92be2f72018-06-19 14:33:47 -0400150 cgp.kInCircleEdge.name());
Brian Salomon45c92202018-04-10 10:53:58 -0400151 }
joshualittabb52a12015-01-13 15:02:10 -0800152
joshualittb8c241a2015-05-19 08:23:30 -0700153 // setup pass through color
Brian Salomon92be2f72018-06-19 14:33:47 -0400154 varyingHandler->addPassThroughAttribute(cgp.kInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800155
joshualittabb52a12015-01-13 15:02:10 -0800156 // Setup position
Brian Salomon92be2f72018-06-19 14:33:47 -0400157 this->writeOutputPosition(vertBuilder, gpArgs, cgp.kInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800158
159 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800160 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800161 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800162 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400163 cgp.kInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700164 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700165 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800166
Ethan Nicholas8aa45692017-09-20 11:24:15 -0400167 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400168 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
169 fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800170 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500171 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400172 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
173 fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800174 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000175 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000176
Brian Salomon92be2f72018-06-19 14:33:47 -0400177 if (cgp.fInClipPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppend(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400179 "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
Brian Salomon289e3d82016-12-14 15:52:56 -0500180 "clipPlane.z, 0.0, 1.0);");
Brian Salomon92be2f72018-06-19 14:33:47 -0400181 if (cgp.fInIsectPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500182 fragBuilder->codeAppend(
183 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
184 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700185 }
Brian Salomon92be2f72018-06-19 14:33:47 -0400186 if (cgp.fInUnionPlane.isInitialized()) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500187 fragBuilder->codeAppend(
188 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
189 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 }
191 fragBuilder->codeAppend("edgeAlpha *= clip;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400192 if (cgp.fInRoundCapCenters.isInitialized()) {
Brian Salomon33c44222018-04-19 08:44:21 -0400193 // We compute coverage of the round caps as circles at the butt caps produced
194 // by the clip planes. The inverse of the clip planes is applied so that there
195 // is no double counting.
Brian Salomon45c92202018-04-10 10:53:58 -0400196 fragBuilder->codeAppendf(
197 "half dcap1 = circleEdge.z * (%s - length(circleEdge.xy - "
198 " roundCapCenters.xy));"
199 "half dcap2 = circleEdge.z * (%s - length(circleEdge.xy - "
200 " roundCapCenters.zw));"
Brian Salomon33c44222018-04-19 08:44:21 -0400201 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
Brian Salomon45c92202018-04-10 10:53:58 -0400202 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
203 capRadius.fsIn(), capRadius.fsIn());
204 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700205 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000206 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207 }
208
robertphillips46d36f02015-01-18 08:14:14 -0800209 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500210 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700211 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800212 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700215 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon92be2f72018-06-19 14:33:47 -0400216 key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
217 key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
218 key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
219 key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700220 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 }
222
bsalomona624bf32016-09-20 09:12:47 -0700223 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
224 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700225 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700226 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700227 }
228
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229 private:
egdaniele659a582015-11-13 09:55:43 -0800230 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 };
232
Brian Salomon92be2f72018-06-19 14:33:47 -0400233 const Attribute& onVertexAttribute(int i) const override {
234 return IthInitializedAttribute(i, kInPosition, kInColor, kInCircleEdge, fInClipPlane,
235 fInIsectPlane, fInUnionPlane, fInRoundCapCenters);
236 }
237
Brian Salomon289e3d82016-12-14 15:52:56 -0500238 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400239
240 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
241 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
242 static constexpr Attribute kInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType};
243
244 // Optional attributes.
245 Attribute fInClipPlane;
246 Attribute fInIsectPlane;
247 Attribute fInUnionPlane;
248 Attribute fInRoundCapCenters;
249
Brian Salomon289e3d82016-12-14 15:52:56 -0500250 bool fStroke;
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400251 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000252
joshualitt249af152014-09-15 11:41:13 -0700253 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254};
Brian Salomon92be2f72018-06-19 14:33:47 -0400255constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInPosition;
256constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInColor;
257constexpr GrPrimitiveProcessor::Attribute CircleGeometryProcessor::kInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258
bsalomoncdaa97b2016-03-08 08:30:14 -0800259GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000260
Hal Canary6f6961e2017-01-31 13:50:44 -0500261#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700262sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon45c92202018-04-10 10:53:58 -0400263 bool stroke = d->fRandom->nextBool();
264 bool roundCaps = stroke ? d->fRandom->nextBool() : false;
265 bool clipPlane = d->fRandom->nextBool();
266 bool isectPlane = d->fRandom->nextBool();
267 bool unionPlane = d->fRandom->nextBool();
268 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
269 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(stroke, roundCaps, clipPlane,
270 isectPlane, unionPlane, matrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271}
Hal Canary6f6961e2017-01-31 13:50:44 -0500272#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400274class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
275public:
276 ButtCapDashedCircleGeometryProcessor(const SkMatrix& localMatrix)
277 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400278 this->setVertexAttributeCnt(4);
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400279 }
280
281 ~ButtCapDashedCircleGeometryProcessor() override {}
282
283 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
284
285 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
286 GLSLProcessor::GenKey(*this, caps, b);
287 }
288
289 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
290 return new GLSLProcessor();
291 }
292
293private:
294 class GLSLProcessor : public GrGLSLGeometryProcessor {
295 public:
296 GLSLProcessor() {}
297
298 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
299 const ButtCapDashedCircleGeometryProcessor& bcscgp =
300 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
301 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
302 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
303 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
304 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
305
306 // emit attributes
307 varyingHandler->emitAttributes(bcscgp);
308 fragBuilder->codeAppend("float4 circleEdge;");
Brian Salomon92be2f72018-06-19 14:33:47 -0400309 varyingHandler->addPassThroughAttribute(bcscgp.kInCircleEdge, "circleEdge");
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400310
311 fragBuilder->codeAppend("float4 dashParams;");
312 varyingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400313 bcscgp.kInDashParams, "dashParams",
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400314 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
315 GrGLSLVarying wrapDashes(kHalf4_GrSLType);
316 varyingHandler->addVarying("wrapDashes", &wrapDashes,
317 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
318 GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
319 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
320 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
Brian Salomon92be2f72018-06-19 14:33:47 -0400321 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.kInDashParams.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400322 // Our fragment shader works in on/off intervals as specified by dashParams.xy:
323 // x = length of on interval, y = length of on + off.
324 // There are two other parameters in dashParams.zw:
325 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
326 // Each interval has a "corresponding" dash which may be shifted partially or
327 // fully out of its interval by the phase. So there may be up to two "visual"
328 // dashes in an interval.
329 // When computing coverage in an interval we look at three dashes. These are the
330 // "corresponding" dashes from the current, previous, and next intervals. Any of these
331 // may be phase shifted into our interval or even when phase=0 they may be within half a
332 // pixel distance of a pixel center in the interval.
333 // When in the first interval we need to check the dash from the last interval. And
334 // similarly when in the last interval we need to check the dash from the first
335 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
336 // We compute the dash begin/end angles in the vertex shader and apply them in the
337 // fragment shader when we detect we're in the first/last interval.
338 vertBuilder->codeAppend(R"(
339 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
340 // to the fragment shader as a varying.
341 float4 wrapDashes;
342 half lastIntervalLength = mod(6.28318530718, dashParams.y);
343 // We can happen to be perfectly divisible.
344 if (0 == lastIntervalLength) {
345 lastIntervalLength = dashParams.y;
346 }
347 // Let 'l' be the last interval before reaching 2 pi.
348 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
349 // "corresponding" dash appears in the l-th interval and is closest to the 0-th
350 // interval.
351 half offset = 0;
352 if (-dashParams.w >= lastIntervalLength) {
353 offset = -dashParams.y;
354 } else if (dashParams.w > dashParams.y - lastIntervalLength) {
355 offset = dashParams.y;
356 }
357 wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
358 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
359 // min.
360 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
361
362 // Based on the phase determine whether the -1st, 0th, or 1st interval's
363 // "corresponding" dash appears in the 0th interval and is closest to l.
364 offset = 0;
365 if (dashParams.w >= dashParams.x) {
366 offset = dashParams.y;
367 } else if (-dashParams.w > dashParams.y - dashParams.x) {
368 offset = -dashParams.y;
369 }
370 wrapDashes.z = lastIntervalLength + offset - dashParams.w;
371 wrapDashes.w = wrapDashes.z + dashParams.x;
372 // The start of the dash we're considering may be clipped by the start of the
373 // circle.
374 wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
375 )");
376 vertBuilder->codeAppendf("%s = wrapDashes;", wrapDashes.vsOut());
377 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
378 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
379 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
380
381 // setup pass through color
382 varyingHandler->addPassThroughAttribute(
Brian Salomon92be2f72018-06-19 14:33:47 -0400383 bcscgp.kInColor, args.fOutputColor,
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400384 GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
385
386 // Setup position
Brian Salomon92be2f72018-06-19 14:33:47 -0400387 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.kInPosition.name());
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400388
389 // emit transforms
390 this->emitTransforms(vertBuilder,
391 varyingHandler,
392 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400393 bcscgp.kInPosition.asShaderVar(),
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400394 bcscgp.fLocalMatrix,
395 args.fFPCoordTransformHandler);
396 GrShaderVar fnArgs[] = {
397 GrShaderVar("angleToEdge", kFloat_GrSLType),
398 GrShaderVar("diameter", kFloat_GrSLType),
399 };
400 SkString fnName;
401 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
402 SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
403 float linearDist;
404 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
405 linearDist = diameter * sin(angleToEdge / 2);
406 return clamp(linearDist + 0.5, 0, 1);
407 )",
408 &fnName);
409 fragBuilder->codeAppend(R"(
410 float d = length(circleEdge.xy) * circleEdge.z;
411
412 // Compute coverage from outer/inner edges of the stroke.
413 half distanceToOuterEdge = circleEdge.z - d;
414 half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);
415 half distanceToInnerEdge = d - circleEdge.z * circleEdge.w;
416 half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);
417 edgeAlpha *= innerAlpha;
418
419 half angleFromStart = atan(circleEdge.y, circleEdge.x) - dashParams.z;
420 angleFromStart = mod(angleFromStart, 6.28318530718);
421 float x = mod(angleFromStart, dashParams.y);
422 // Convert the radial distance from center to pixel into a diameter.
423 d *= 2;
424 half2 currDash = half2(-dashParams.w, dashParams.x - dashParams.w);
425 half2 nextDash = half2(dashParams.y - dashParams.w,
426 dashParams.y + dashParams.x - dashParams.w);
427 half2 prevDash = half2(-dashParams.y - dashParams.w,
428 -dashParams.y + dashParams.x - dashParams.w);
429 half dashAlpha = 0;
430 )");
431 fragBuilder->codeAppendf(R"(
432 if (angleFromStart - x + dashParams.y >= 6.28318530718) {
433 dashAlpha += %s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d);
434 currDash.y = min(currDash.y, lastIntervalLength);
435 if (nextDash.x >= lastIntervalLength) {
436 // The next dash is outside the 0..2pi range, throw it away
437 nextDash.xy = half2(1000);
438 } else {
439 // Clip the end of the next dash to the end of the circle
440 nextDash.y = min(nextDash.y, lastIntervalLength);
441 }
442 }
443 )", fnName.c_str(), fnName.c_str());
444 fragBuilder->codeAppendf(R"(
445 if (angleFromStart - x - dashParams.y < -0.01) {
446 dashAlpha += %s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d);
447 currDash.x = max(currDash.x, 0);
448 if (prevDash.y <= 0) {
449 // The previous dash is outside the 0..2pi range, throw it away
450 prevDash.xy = half2(1000);
451 } else {
452 // Clip the start previous dash to the start of the circle
453 prevDash.x = max(prevDash.x, 0);
454 }
455 }
456 )", fnName.c_str(), fnName.c_str());
457 fragBuilder->codeAppendf(R"(
458 dashAlpha += %s(x - currDash.x, d) * %s(currDash.y - x, d);
459 dashAlpha += %s(x - nextDash.x, d) * %s(nextDash.y - x, d);
460 dashAlpha += %s(x - prevDash.x, d) * %s(prevDash.y - x, d);
461 dashAlpha = min(dashAlpha, 1);
462 edgeAlpha *= dashAlpha;
463 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
464 fnName.c_str());
465 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
466 }
467
468 static void GenKey(const GrGeometryProcessor& gp,
469 const GrShaderCaps&,
470 GrProcessorKeyBuilder* b) {
471 const ButtCapDashedCircleGeometryProcessor& bcscgp =
472 gp.cast<ButtCapDashedCircleGeometryProcessor>();
473 b->add32(bcscgp.fLocalMatrix.hasPerspective());
474 }
475
476 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
477 FPCoordTransformIter&& transformIter) override {
478 this->setTransformDataHelper(
479 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
480 &transformIter);
481 }
482
483 private:
484 typedef GrGLSLGeometryProcessor INHERITED;
485 };
486
Brian Salomon92be2f72018-06-19 14:33:47 -0400487 const Attribute& onVertexAttribute(int i) const override {
488 return IthAttribute(i, kInPosition, kInColor, kInCircleEdge, kInDashParams);
489 }
490
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400491 SkMatrix fLocalMatrix;
Brian Salomon92be2f72018-06-19 14:33:47 -0400492 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
493 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
494 static constexpr Attribute kInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType};
495 static constexpr Attribute kInDashParams = {"inDashParams", kFloat4_GrVertexAttribType};
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400496
497 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
498
499 typedef GrGeometryProcessor INHERITED;
500};
Brian Salomon92be2f72018-06-19 14:33:47 -0400501constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInPosition;
502constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInColor;
503constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInCircleEdge;
504constexpr GrPrimitiveProcessor::Attribute ButtCapDashedCircleGeometryProcessor::kInDashParams;
Brian Salomon62e4f3d2018-04-20 13:54:11 -0400505
506#if GR_TEST_UTILS
507sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
508 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
509 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(matrix));
510}
511#endif
512
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000513///////////////////////////////////////////////////////////////////////////////
514
515/**
516 * 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 +0000517 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
518 * in both x and y directions.
519 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000520 * 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 +0000521 */
522
bsalomoncdaa97b2016-03-08 08:30:14 -0800523class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000524public:
Brian Salomonea26d6b2018-01-23 20:33:21 +0000525 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
526 : INHERITED(kEllipseGeometryProcessor_ClassID)
527 , fLocalMatrix(localMatrix) {
Brian Salomon92be2f72018-06-19 14:33:47 -0400528 this->setVertexAttributeCnt(4);
Brian Salomonea26d6b2018-01-23 20:33:21 +0000529 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000530 }
531
Brian Salomond3b65972017-03-22 12:05:03 -0400532 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000533
mtklein36352bf2015-03-25 18:17:31 -0700534 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800535
Brian Salomon94efbf52016-11-29 13:43:05 -0500536 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700537 GLSLProcessor::GenKey(*this, caps, b);
538 }
539
Brian Salomon94efbf52016-11-29 13:43:05 -0500540 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700541 return new GLSLProcessor();
542 }
543
544private:
egdaniel57d3b032015-11-13 11:57:27 -0800545 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000546 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800547 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000548
Brian Salomon289e3d82016-12-14 15:52:56 -0500549 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800550 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800551 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800552 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800553 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000554
joshualittabb52a12015-01-13 15:02:10 -0800555 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800556 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800557
Chris Dalton27372882017-12-08 13:34:21 -0700558 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800559 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800560 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
Brian Salomon92be2f72018-06-19 14:33:47 -0400561 egp.kInEllipseOffset.name());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000562
Chris Dalton27372882017-12-08 13:34:21 -0700563 GrGLSLVarying ellipseRadii(kHalf4_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800564 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon92be2f72018-06-19 14:33:47 -0400565 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.kInEllipseRadii.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800566
Chris Dalton60283612018-02-14 13:38:14 -0700567 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700568 // setup pass through color
Brian Salomon92be2f72018-06-19 14:33:47 -0400569 varyingHandler->addPassThroughAttribute(egp.kInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800570
joshualittabb52a12015-01-13 15:02:10 -0800571 // Setup position
Brian Salomon92be2f72018-06-19 14:33:47 -0400572 this->writeOutputPosition(vertBuilder, gpArgs, egp.kInPosition.name());
joshualittabb52a12015-01-13 15:02:10 -0800573
574 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800575 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800576 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800577 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400578 egp.kInPosition.asShaderVar(),
bsalomon31df31c2016-08-17 09:00:24 -0700579 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700580 args.fFPCoordTransformHandler);
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400581 // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
582 // to compute both the edges because we need two separate test equations for
583 // the single offset.
584 // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
585 // the distance by the gradient, non-uniformly scaled by the inverse of the
586 // ellipse size.
joshualitt4973d9d2014-11-08 09:24:25 -0800587
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000588 // for outer curve
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400589 fragBuilder->codeAppendf("half2 offset = %s;", ellipseOffsets.fsIn());
590 if (egp.fStroke) {
591 fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
592 }
593 fragBuilder->codeAppend("half test = dot(offset, offset) - 1.0;");
594 fragBuilder->codeAppendf("half2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400595 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700596
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000597 // avoid calling inversesqrt on zero.
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400598 if (args.fShaderCaps->halfIs32Bits()) {
599 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-6);");
600 } else {
Jim Van Verth1de44392018-06-28 12:46:23 -0400601 fragBuilder->codeAppend("grad_dot = max(grad_dot, 5.0e-5);");
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400602 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400603 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
604 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000605
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000606 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800607 if (egp.fStroke) {
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400608 fragBuilder->codeAppendf("offset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800609 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400610 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
611 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800612 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
613 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000614 }
615
Brian Salomonea26d6b2018-01-23 20:33:21 +0000616 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000617 }
618
robertphillips46d36f02015-01-18 08:14:14 -0800619 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500620 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700621 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800622 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
623 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700624 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700625 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000626 }
627
bsalomona624bf32016-09-20 09:12:47 -0700628 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
629 FPCoordTransformIter&& transformIter) override {
630 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
631 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700632 }
633
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000634 private:
egdaniele659a582015-11-13 09:55:43 -0800635 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000636 };
637
Brian Salomon92be2f72018-06-19 14:33:47 -0400638 const Attribute& onVertexAttribute(int i) const override {
639 return IthAttribute(i, kInPosition, kInColor, kInEllipseOffset, kInEllipseRadii);
640 }
641
642 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
643 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
644 static constexpr Attribute kInEllipseOffset = {"inEllipseOffset", kHalf2_GrVertexAttribType};
645 static constexpr Attribute kInEllipseRadii = {"inEllipseRadii", kHalf4_GrVertexAttribType};
646
joshualitte3ababe2015-05-15 07:56:07 -0700647 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000648 bool fStroke;
649
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400650 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000651
joshualitt249af152014-09-15 11:41:13 -0700652 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000653};
Brian Salomon92be2f72018-06-19 14:33:47 -0400654constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInPosition;
655constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInColor;
656constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseOffset;
657constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000658
bsalomoncdaa97b2016-03-08 08:30:14 -0800659GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000660
Hal Canary6f6961e2017-01-31 13:50:44 -0500661#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700662sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000663 return sk_sp<GrGeometryProcessor>(
664 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000665}
Hal Canary6f6961e2017-01-31 13:50:44 -0500666#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000667
668///////////////////////////////////////////////////////////////////////////////
669
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000670/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000671 * 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 +0000672 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
673 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
674 * using differentials.
675 *
676 * The result is device-independent and can be used with any affine matrix.
677 */
678
bsalomoncdaa97b2016-03-08 08:30:14 -0800679enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000680
bsalomoncdaa97b2016-03-08 08:30:14 -0800681class DIEllipseGeometryProcessor : public GrGeometryProcessor {
682public:
Brian Salomonea26d6b2018-01-23 20:33:21 +0000683 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400684 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000685 , fViewMatrix(viewMatrix) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000686 fStyle = style;
Brian Salomon92be2f72018-06-19 14:33:47 -0400687 this->setVertexAttributeCnt(4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000688 }
689
Brian Salomond3b65972017-03-22 12:05:03 -0400690 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000691
mtklein36352bf2015-03-25 18:17:31 -0700692 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000693
Brian Salomon94efbf52016-11-29 13:43:05 -0500694 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700695 GLSLProcessor::GenKey(*this, caps, b);
696 }
halcanary9d524f22016-03-29 09:03:52 -0700697
Brian Salomon94efbf52016-11-29 13:43:05 -0500698 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700699 return new GLSLProcessor();
700 }
701
702private:
egdaniel57d3b032015-11-13 11:57:27 -0800703 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000704 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500705 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000706
joshualitt465283c2015-09-11 08:19:35 -0700707 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800708 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800709 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800710 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800711 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000712
joshualittabb52a12015-01-13 15:02:10 -0800713 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800714 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800715
Chris Dalton27372882017-12-08 13:34:21 -0700716 GrGLSLVarying offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800717 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon92be2f72018-06-19 14:33:47 -0400718 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.kInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700719
Chris Dalton27372882017-12-08 13:34:21 -0700720 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800721 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon92be2f72018-06-19 14:33:47 -0400722 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.kInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800723
Chris Dalton60283612018-02-14 13:38:14 -0700724 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Salomon92be2f72018-06-19 14:33:47 -0400725 varyingHandler->addPassThroughAttribute(diegp.kInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800726
joshualittabb52a12015-01-13 15:02:10 -0800727 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400728 this->writeOutputPosition(vertBuilder,
729 uniformHandler,
730 gpArgs,
Brian Salomon92be2f72018-06-19 14:33:47 -0400731 diegp.kInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400732 diegp.fViewMatrix,
733 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800734
735 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800736 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800737 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800738 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400739 diegp.kInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700740 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800741
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000742 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400743 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
744 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
745 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
746 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500747 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400748 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
749 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500750 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000751
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400752 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000753 // avoid calling inversesqrt on zero.
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400754 if (args.fShaderCaps->halfIs32Bits()) {
755 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-6);");
756 } else {
Jim Van Verth1de44392018-06-28 12:46:23 -0400757 fragBuilder->codeAppend("grad_dot = max(grad_dot, 5.0e-5);");
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400758 }
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400759 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800760 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000761 // can probably do this with one step
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400762 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800763 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000764 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400765 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000766 }
767
768 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800769 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800770 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
771 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
772 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
773 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500774 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400775 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
776 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500777 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800778 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
779 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000780 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000781
782 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000783 }
784
robertphillips46d36f02015-01-18 08:14:14 -0800785 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500786 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700787 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800788 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
789 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700790 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700791 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000792 }
793
bsalomona624bf32016-09-20 09:12:47 -0700794 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
795 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800796 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700797
bsalomon31df31c2016-08-17 09:00:24 -0700798 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
799 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700800 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800801 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700802 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
803 }
bsalomona624bf32016-09-20 09:12:47 -0700804 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000805 }
806
807 private:
joshualitt5559ca22015-05-21 15:50:36 -0700808 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700809 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800810
egdaniele659a582015-11-13 09:55:43 -0800811 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000812 };
813
Brian Salomon92be2f72018-06-19 14:33:47 -0400814 const Attribute& onVertexAttribute(int i) const override {
815 return IthAttribute(i, kInPosition, kInColor, kInEllipseOffsets0, kInEllipseOffsets1);
816 }
817
818 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
819 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
820 static constexpr Attribute kInEllipseOffsets0 = {"inEllipseOffsets0",
821 kHalf2_GrVertexAttribType};
822 static constexpr Attribute kInEllipseOffsets1 = {"inEllipseOffsets1",
823 kHalf2_GrVertexAttribType};
824
Brian Salomon289e3d82016-12-14 15:52:56 -0500825 SkMatrix fViewMatrix;
826 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000827
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400828 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000829
joshualitt249af152014-09-15 11:41:13 -0700830 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000831};
Brian Salomon92be2f72018-06-19 14:33:47 -0400832constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInPosition;
833constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInColor;
834constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets0;
835constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000836
bsalomoncdaa97b2016-03-08 08:30:14 -0800837GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000838
Hal Canary6f6961e2017-01-31 13:50:44 -0500839#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700840sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500841 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Salomonea26d6b2018-01-23 20:33:21 +0000842 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000843}
Hal Canary6f6961e2017-01-31 13:50:44 -0500844#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000845
846///////////////////////////////////////////////////////////////////////////////
847
jvanverth6ca48822016-10-07 06:57:32 -0700848// We have two possible cases for geometry for a circle:
849
850// In the case of a normal fill, we draw geometry for the circle as an octagon.
851static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500852 // enter the octagon
853 // clang-format off
854 0, 1, 8, 1, 2, 8,
855 2, 3, 8, 3, 4, 8,
856 4, 5, 8, 5, 6, 8,
857 6, 7, 8, 7, 0, 8
858 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700859};
860
861// For stroked circles, we use two nested octagons.
862static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500863 // enter the octagon
864 // clang-format off
865 0, 1, 9, 0, 9, 8,
866 1, 2, 10, 1, 10, 9,
867 2, 3, 11, 2, 11, 10,
868 3, 4, 12, 3, 12, 11,
869 4, 5, 13, 4, 13, 12,
870 5, 6, 14, 5, 14, 13,
871 6, 7, 15, 6, 15, 14,
872 7, 0, 8, 7, 8, 15,
873 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700874};
875
Brian Salomon289e3d82016-12-14 15:52:56 -0500876
jvanverth6ca48822016-10-07 06:57:32 -0700877static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
878static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
879static const int kVertsPerStrokeCircle = 16;
880static const int kVertsPerFillCircle = 9;
881
882static int circle_type_to_vert_count(bool stroked) {
883 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
884}
885
886static int circle_type_to_index_count(bool stroked) {
887 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
888}
889
890static const uint16_t* circle_type_to_indices(bool stroked) {
891 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
892}
893
894///////////////////////////////////////////////////////////////////////////////
895
Brian Salomon05441c42017-05-15 16:45:49 -0400896class CircleOp final : public GrMeshDrawOp {
897private:
898 using Helper = GrSimpleMeshDrawOpHelper;
899
joshualitt76e7fb62015-02-11 08:52:27 -0800900public:
Brian Salomon25a88092016-12-01 09:36:50 -0500901 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700902
bsalomon4f3a0ca2016-08-22 13:14:26 -0700903 /** Optional extra params to render a partial arc rather than a full circle. */
904 struct ArcParams {
905 SkScalar fStartAngleRadians;
906 SkScalar fSweepAngleRadians;
907 bool fUseCenter;
908 };
Brian Salomon05441c42017-05-15 16:45:49 -0400909
Robert Phillips7c525e62018-06-12 10:11:12 -0400910 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
911 GrPaint&& paint,
912 const SkMatrix& viewMatrix,
913 SkPoint center,
914 SkScalar radius,
915 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -0400916 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700917 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700918 if (style.hasPathEffect()) {
919 return nullptr;
920 }
Brian Salomon05441c42017-05-15 16:45:49 -0400921 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700922 SkStrokeRec::Style recStyle = stroke.getStyle();
923 if (arcParams) {
924 // Arc support depends on the style.
925 switch (recStyle) {
926 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500927 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700928 return nullptr;
929 case SkStrokeRec::kFill_Style:
930 // This supports all fills.
931 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400932 case SkStrokeRec::kStroke_Style:
933 // Strokes that don't use the center point are supported with butt and round
934 // caps.
935 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
936 return nullptr;
937 }
938 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700939 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400940 // Hairline only supports butt cap. Round caps could be emulated by slightly
941 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700942 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
943 return nullptr;
944 }
945 break;
946 }
947 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400948 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
949 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400950 }
951
Brian Salomonea26d6b2018-01-23 20:33:21 +0000952 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400953 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000954 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400955 const SkStrokeRec& stroke = style.strokeRec();
956 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700957
Brian Salomon45c92202018-04-10 10:53:58 -0400958 fRoundCaps = false;
959
bsalomon4b4a7cc2016-07-08 04:42:54 -0700960 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700961 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700962 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800963
Brian Salomon289e3d82016-12-14 15:52:56 -0500964 bool isStrokeOnly =
965 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700966 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700967
jvanverth6ca48822016-10-07 06:57:32 -0700968 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700969 SkScalar outerRadius = radius;
970 SkScalar halfWidth = 0;
971 if (hasStroke) {
972 if (SkScalarNearlyZero(strokeWidth)) {
973 halfWidth = SK_ScalarHalf;
974 } else {
975 halfWidth = SkScalarHalf(strokeWidth);
976 }
977
978 outerRadius += halfWidth;
979 if (isStrokeOnly) {
980 innerRadius = radius - halfWidth;
981 }
982 }
983
984 // The radii are outset for two reasons. First, it allows the shader to simply perform
985 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
986 // Second, the outer radius is used to compute the verts of the bounding box that is
987 // rendered and the outset ensures the box will cover all partially covered by the circle.
988 outerRadius += SK_ScalarHalf;
989 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700990 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400991 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700992
bsalomon4f3a0ca2016-08-22 13:14:26 -0700993 // This makes every point fully inside the intersection plane.
994 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
995 // This makes every point fully outside the union plane.
996 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400997 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700998 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
999 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001000 if (arcParams) {
1001 // The shader operates in a space where the circle is translated to be centered at the
1002 // origin. Here we compute points on the unit circle at the starting and ending angles.
1003 SkPoint startPoint, stopPoint;
1004 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
1005 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
1006 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001007
1008 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1009 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1010 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1011 startPoint.normalize();
1012 stopPoint.normalize();
1013
1014 // If the matrix included scale (on one axis) we need to swap our start and end points
1015 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001016 using std::swap;
1017 swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001018 }
1019
Brian Salomon45c92202018-04-10 10:53:58 -04001020 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1021 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1022 SkPoint roundCaps[2];
1023 if (fRoundCaps) {
1024 // Compute the cap center points in the normalized space.
1025 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1026 roundCaps[0] = startPoint * midRadius;
1027 roundCaps[1] = stopPoint * midRadius;
1028 } else {
1029 roundCaps[0] = kUnusedRoundCaps[0];
1030 roundCaps[1] = kUnusedRoundCaps[1];
1031 }
1032
bsalomon4f3a0ca2016-08-22 13:14:26 -07001033 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001034 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1035 // center of the butts.
1036 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001037 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001038 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001039 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001040 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1041 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1042 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001043 if (useCenter) {
1044 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1045 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
1046 if (arcParams->fSweepAngleRadians > 0) {
1047 norm0.negate();
1048 } else {
1049 norm1.negate();
1050 }
Brian Salomon05441c42017-05-15 16:45:49 -04001051 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001052 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001053 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001054 color,
1055 innerRadius,
1056 outerRadius,
1057 {norm0.fX, norm0.fY, 0.5f},
1058 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1059 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001060 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001061 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001062 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001063 fClipPlaneIsect = false;
1064 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001065 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001066 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001067 color,
1068 innerRadius,
1069 outerRadius,
1070 {norm0.fX, norm0.fY, 0.5f},
1071 {norm1.fX, norm1.fY, 0.5f},
1072 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001073 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001074 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001075 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001076 fClipPlaneIsect = true;
1077 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001078 }
1079 } else {
1080 // We clip to a secant of the original circle.
1081 startPoint.scale(radius);
1082 stopPoint.scale(radius);
1083 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1084 norm.normalize();
1085 if (arcParams->fSweepAngleRadians > 0) {
1086 norm.negate();
1087 }
1088 SkScalar d = -norm.dot(startPoint) + 0.5f;
1089
Brian Salomon05441c42017-05-15 16:45:49 -04001090 fCircles.emplace_back(
1091 Circle{color,
1092 innerRadius,
1093 outerRadius,
1094 {norm.fX, norm.fY, d},
1095 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1096 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001097 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001098 devBounds,
1099 stroked});
1100 fClipPlane = true;
1101 fClipPlaneIsect = false;
1102 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001103 }
1104 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001105 fCircles.emplace_back(
1106 Circle{color,
1107 innerRadius,
1108 outerRadius,
1109 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1110 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1111 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001112 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001113 devBounds,
1114 stroked});
1115 fClipPlane = false;
1116 fClipPlaneIsect = false;
1117 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001118 }
bsalomon88cf17d2016-07-08 06:40:56 -07001119 // Use the original radius and stroke radius for the bounds so that it does not include the
1120 // AA bloat.
1121 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001122 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001123 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1124 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001125 fVertCount = circle_type_to_vert_count(stroked);
1126 fIndexCount = circle_type_to_index_count(stroked);
1127 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001128 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001129
Brian Salomon289e3d82016-12-14 15:52:56 -05001130 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001131
Robert Phillipsf1748f52017-09-14 14:11:24 -04001132 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001133 fHelper.visitProxies(func);
1134 }
1135
robertphillipse004bfc2015-11-16 09:06:59 -08001136 SkString dumpInfo() const override {
1137 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001138 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001139 string.appendf(
1140 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1141 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001142 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1143 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1144 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001145 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001146 string += fHelper.dumpInfo();
1147 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001148 return string;
1149 }
1150
Brian Osman9a725dd2017-09-20 09:53:22 -04001151 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1152 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001153 GrColor* color = &fCircles.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001154 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1155 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001156 }
1157
1158 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1159
bsalomone46f9fe2015-08-18 06:05:14 -07001160private:
Brian Salomon91326c32017-08-09 16:02:19 -04001161 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001162 SkMatrix localMatrix;
1163 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001164 return;
1165 }
1166
1167 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001168 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Salomon45c92202018-04-10 10:53:58 -04001169 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001170
1171 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001172 SkPoint fPos;
1173 GrColor fColor;
1174 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001175 SkScalar fOuterRadius;
1176 SkScalar fInnerRadius;
1177 // These planes may or may not be present in the vertex buffer.
1178 SkScalar fHalfPlanes[3][3];
1179 };
joshualitt76e7fb62015-02-11 08:52:27 -08001180
Brian Salomon45c92202018-04-10 10:53:58 -04001181 int numPlanes = (int)fClipPlane + fClipPlaneIsect + fClipPlaneUnion;
1182 auto vertexCapCenters = [numPlanes](CircleVertex* v) {
1183 return (void*)(v->fHalfPlanes + numPlanes);
1184 };
Brian Salomon92be2f72018-06-19 14:33:47 -04001185 size_t vertexStride = sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
1186 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
1187 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
1188 (fRoundCaps ? 2 * sizeof(SkPoint) : 0);
1189 SkASSERT(vertexStride == gp->debugOnly_vertexStride());
jvanverth6ca48822016-10-07 06:57:32 -07001190
1191 const GrBuffer* vertexBuffer;
1192 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -05001193 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1194 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001195 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -07001196 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001197 return;
1198 }
1199
jvanverth6ca48822016-10-07 06:57:32 -07001200 const GrBuffer* indexBuffer = nullptr;
1201 int firstIndex = 0;
1202 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1203 if (!indices) {
1204 SkDebugf("Could not allocate indices\n");
1205 return;
1206 }
1207
1208 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001209 for (const auto& circle : fCircles) {
1210 SkScalar innerRadius = circle.fInnerRadius;
1211 SkScalar outerRadius = circle.fOuterRadius;
1212 GrColor color = circle.fColor;
1213 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001214
Brian Salomon289e3d82016-12-14 15:52:56 -05001215 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
1216 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
1217 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
1218 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
1219 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
1220 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
1221 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
1222 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001223
1224 // The inner radius in the vertex data must be specified in normalized space.
1225 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001226
1227 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001228 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -07001229 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -05001230
Brian Salomon289e3d82016-12-14 15:52:56 -05001231 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001232 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001233 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001234 v0->fOuterRadius = outerRadius;
1235 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001236
Brian Salomon289e3d82016-12-14 15:52:56 -05001237 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001238 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001239 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001240 v1->fOuterRadius = outerRadius;
1241 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001242
Brian Salomon289e3d82016-12-14 15:52:56 -05001243 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001244 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001245 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001246 v2->fOuterRadius = outerRadius;
1247 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001248
Brian Salomon289e3d82016-12-14 15:52:56 -05001249 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001250 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001251 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001252 v3->fOuterRadius = outerRadius;
1253 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001254
Brian Salomon289e3d82016-12-14 15:52:56 -05001255 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001256 v4->fColor = color;
1257 v4->fOffset = SkPoint::Make(octOffset, 1);
1258 v4->fOuterRadius = outerRadius;
1259 v4->fInnerRadius = innerRadius;
1260
Brian Salomon289e3d82016-12-14 15:52:56 -05001261 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001262 v5->fColor = color;
1263 v5->fOffset = SkPoint::Make(-octOffset, 1);
1264 v5->fOuterRadius = outerRadius;
1265 v5->fInnerRadius = innerRadius;
1266
Brian Salomon289e3d82016-12-14 15:52:56 -05001267 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001268 v6->fColor = color;
1269 v6->fOffset = SkPoint::Make(-1, octOffset);
1270 v6->fOuterRadius = outerRadius;
1271 v6->fInnerRadius = innerRadius;
1272
Brian Salomon289e3d82016-12-14 15:52:56 -05001273 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001274 v7->fColor = color;
1275 v7->fOffset = SkPoint::Make(-1, -octOffset);
1276 v7->fOuterRadius = outerRadius;
1277 v7->fInnerRadius = innerRadius;
1278
bsalomon4f3a0ca2016-08-22 13:14:26 -07001279 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001280 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1281 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1282 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1283 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1284 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1285 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1286 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1287 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001288 }
1289 int unionIdx = 1;
1290 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001291 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1292 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1293 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1294 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1295 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1296 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1297 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1298 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001299 unionIdx = 2;
1300 }
1301 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001302 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1303 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1304 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1305 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1306 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1307 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1308 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1309 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001310 }
Brian Salomon45c92202018-04-10 10:53:58 -04001311 if (fRoundCaps) {
1312 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1313 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1314 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1315 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1316 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1317 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1318 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1319 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1320 }
jvanverth6ca48822016-10-07 06:57:32 -07001321
Brian Salomon05441c42017-05-15 16:45:49 -04001322 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001323 // compute the inner ring
1324 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1325 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
1326 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
1327 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
1328 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
1329 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
1330 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
1331 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
1332
1333 // cosine and sine of pi/8
1334 SkScalar c = 0.923579533f;
1335 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -04001336 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001337
Brian Salomon289e3d82016-12-14 15:52:56 -05001338 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001339 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001340 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001341 v0->fOuterRadius = outerRadius;
1342 v0->fInnerRadius = innerRadius;
1343
Brian Salomon289e3d82016-12-14 15:52:56 -05001344 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001345 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001346 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001347 v1->fOuterRadius = outerRadius;
1348 v1->fInnerRadius = innerRadius;
1349
Brian Salomon289e3d82016-12-14 15:52:56 -05001350 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001351 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001352 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001353 v2->fOuterRadius = outerRadius;
1354 v2->fInnerRadius = innerRadius;
1355
Brian Salomon289e3d82016-12-14 15:52:56 -05001356 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001357 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001358 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001359 v3->fOuterRadius = outerRadius;
1360 v3->fInnerRadius = innerRadius;
1361
Brian Salomon289e3d82016-12-14 15:52:56 -05001362 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001363 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001364 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001365 v4->fOuterRadius = outerRadius;
1366 v4->fInnerRadius = innerRadius;
1367
Brian Salomon289e3d82016-12-14 15:52:56 -05001368 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001369 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001370 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001371 v5->fOuterRadius = outerRadius;
1372 v5->fInnerRadius = innerRadius;
1373
Brian Salomon289e3d82016-12-14 15:52:56 -05001374 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001375 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001376 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001377 v6->fOuterRadius = outerRadius;
1378 v6->fInnerRadius = innerRadius;
1379
Brian Salomon289e3d82016-12-14 15:52:56 -05001380 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001381 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001382 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001383 v7->fOuterRadius = outerRadius;
1384 v7->fInnerRadius = innerRadius;
1385
1386 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001387 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1388 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1389 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1390 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1391 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1392 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1393 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1394 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001395 }
1396 int unionIdx = 1;
1397 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001398 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1399 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1400 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1401 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1402 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1403 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1404 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1405 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001406 unionIdx = 2;
1407 }
1408 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001409 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1410 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1411 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1412 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1413 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1414 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1415 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1416 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001417 }
Brian Salomon45c92202018-04-10 10:53:58 -04001418 if (fRoundCaps) {
1419 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1420 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1421 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1422 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1423 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1424 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1425 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1426 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1427 }
jvanverth6ca48822016-10-07 06:57:32 -07001428 } else {
1429 // filled
1430 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1431 v8->fPos = center;
1432 v8->fColor = color;
1433 v8->fOffset = SkPoint::Make(0, 0);
1434 v8->fOuterRadius = outerRadius;
1435 v8->fInnerRadius = innerRadius;
1436 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001437 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001438 }
1439 int unionIdx = 1;
1440 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001441 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001442 unionIdx = 2;
1443 }
1444 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001445 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001446 }
Brian Salomon45c92202018-04-10 10:53:58 -04001447 SkASSERT(!fRoundCaps);
jvanverth6ca48822016-10-07 06:57:32 -07001448 }
1449
Brian Salomon05441c42017-05-15 16:45:49 -04001450 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1451 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001452 for (int i = 0; i < primIndexCount; ++i) {
1453 *indices++ = primIndices[i] + currStartVertex;
1454 }
1455
Brian Salomon05441c42017-05-15 16:45:49 -04001456 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1457 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001458 }
jvanverth6ca48822016-10-07 06:57:32 -07001459
Chris Dalton3809bab2017-06-13 10:55:06 -06001460 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04001461 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1462 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06001463 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001464 auto pipe = fHelper.makePipeline(target);
1465 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001466 }
1467
Brian Salomon25a88092016-12-01 09:36:50 -05001468 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001469 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001470
1471 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001472 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001473 return false;
1474 }
1475
Brian Salomon05441c42017-05-15 16:45:49 -04001476 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001477 return false;
1478 }
1479
Brian Salomon05441c42017-05-15 16:45:49 -04001480 if (fHelper.usesLocalCoords() &&
1481 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001482 return false;
1483 }
1484
Brian Salomon289e3d82016-12-14 15:52:56 -05001485 // Because we've set up the ops that don't use the planes with noop values
1486 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001487 fClipPlane |= that->fClipPlane;
1488 fClipPlaneIsect |= that->fClipPlaneIsect;
1489 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001490 fRoundCaps |= that->fRoundCaps;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001491
Brian Salomon05441c42017-05-15 16:45:49 -04001492 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001493 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001494 fVertCount += that->fVertCount;
1495 fIndexCount += that->fIndexCount;
1496 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001497 return true;
1498 }
1499
Brian Salomon05441c42017-05-15 16:45:49 -04001500 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001501 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001502 SkScalar fInnerRadius;
1503 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001504 SkScalar fClipPlane[3];
1505 SkScalar fIsectPlane[3];
1506 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001507 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001508 SkRect fDevBounds;
1509 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001510 };
1511
Brian Salomon289e3d82016-12-14 15:52:56 -05001512 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001513 Helper fHelper;
1514 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001515 int fVertCount;
1516 int fIndexCount;
1517 bool fAllFill;
1518 bool fClipPlane;
1519 bool fClipPlaneIsect;
1520 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001521 bool fRoundCaps;
reed1b55a962015-09-17 20:16:13 -07001522
Brian Salomon05441c42017-05-15 16:45:49 -04001523 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001524};
1525
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001526class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1527private:
1528 using Helper = GrSimpleMeshDrawOpHelper;
1529
1530public:
1531 DEFINE_OP_CLASS_ID
1532
Robert Phillips7c525e62018-06-12 10:11:12 -04001533 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1534 GrPaint&& paint,
1535 const SkMatrix& viewMatrix,
1536 SkPoint center,
1537 SkScalar radius,
1538 SkScalar strokeWidth,
1539 SkScalar startAngle,
1540 SkScalar onAngle,
1541 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001542 SkScalar phaseAngle) {
1543 SkASSERT(circle_stays_circle(viewMatrix));
1544 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001545 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1546 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001547 onAngle, offAngle, phaseAngle);
1548 }
1549
1550 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color,
1551 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1552 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1553 SkScalar offAngle, SkScalar phaseAngle)
1554 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1555 SkASSERT(circle_stays_circle(viewMatrix));
1556 viewMatrix.mapPoints(&center, 1);
1557 radius = viewMatrix.mapRadius(radius);
1558 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1559
1560 // Determine the angle where the circle starts in device space and whether its orientation
1561 // has been reversed.
1562 SkVector start;
1563 bool reflection;
1564 if (!startAngle) {
1565 start = {1, 0};
1566 } else {
1567 start.fY = SkScalarSinCos(startAngle, &start.fX);
1568 }
1569 viewMatrix.mapVectors(&start, 1);
1570 startAngle = SkScalarATan2(start.fY, start.fX);
1571 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1572 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1573
1574 auto totalAngle = onAngle + offAngle;
1575 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1576
1577 SkScalar halfWidth = 0;
1578 if (SkScalarNearlyZero(strokeWidth)) {
1579 halfWidth = SK_ScalarHalf;
1580 } else {
1581 halfWidth = SkScalarHalf(strokeWidth);
1582 }
1583
1584 SkScalar outerRadius = radius + halfWidth;
1585 SkScalar innerRadius = radius - halfWidth;
1586
1587 // The radii are outset for two reasons. First, it allows the shader to simply perform
1588 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1589 // Second, the outer radius is used to compute the verts of the bounding box that is
1590 // rendered and the outset ensures the box will cover all partially covered by the circle.
1591 outerRadius += SK_ScalarHalf;
1592 innerRadius -= SK_ScalarHalf;
1593 fViewMatrixIfUsingLocalCoords = viewMatrix;
1594
1595 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1596 center.fX + outerRadius, center.fY + outerRadius);
1597
1598 // We store whether there is a reflection as a negative total angle.
1599 if (reflection) {
1600 totalAngle = -totalAngle;
1601 }
1602 fCircles.push_back(Circle{
1603 color,
1604 outerRadius,
1605 innerRadius,
1606 onAngle,
1607 totalAngle,
1608 startAngle,
1609 phaseAngle,
1610 devBounds
1611 });
1612 // Use the original radius and stroke radius for the bounds so that it does not include the
1613 // AA bloat.
1614 radius += halfWidth;
1615 this->setBounds(
1616 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1617 HasAABloat::kYes, IsZeroArea::kNo);
1618 fVertCount = circle_type_to_vert_count(true);
1619 fIndexCount = circle_type_to_index_count(true);
1620 }
1621
1622 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1623
1624 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); }
1625
1626 SkString dumpInfo() const override {
1627 SkString string;
1628 for (int i = 0; i < fCircles.count(); ++i) {
1629 string.appendf(
1630 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1631 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1632 "Phase: %.2f\n",
1633 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1634 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1635 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle,
1636 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle);
1637 }
1638 string += fHelper.dumpInfo();
1639 string += INHERITED::dumpInfo();
1640 return string;
1641 }
1642
1643 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1644 GrPixelConfigIsClamped dstIsClamped) override {
1645 GrColor* color = &fCircles.front().fColor;
1646 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1647 GrProcessorAnalysisCoverage::kSingleChannel, color);
1648 }
1649
1650 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1651
1652private:
1653 void onPrepareDraws(Target* target) override {
1654 SkMatrix localMatrix;
1655 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1656 return;
1657 }
1658
1659 // Setup geometry processor
1660 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix));
1661
1662 struct CircleVertex {
1663 SkPoint fPos;
1664 GrColor fColor;
1665 SkPoint fOffset;
1666 SkScalar fOuterRadius;
1667 SkScalar fInnerRadius;
1668 SkScalar fOnAngle;
1669 SkScalar fTotalAngle;
1670 SkScalar fStartAngle;
1671 SkScalar fPhaseAngle;
1672 };
1673
Brian Salomon92be2f72018-06-19 14:33:47 -04001674 static constexpr size_t kVertexStride = sizeof(CircleVertex);
1675 SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001676
1677 const GrBuffer* vertexBuffer;
1678 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -04001679 char* vertices = (char*)target->makeVertexSpace(kVertexStride, fVertCount, &vertexBuffer,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001680 &firstVertex);
1681 if (!vertices) {
1682 SkDebugf("Could not allocate vertices\n");
1683 return;
1684 }
1685
1686 const GrBuffer* indexBuffer = nullptr;
1687 int firstIndex = 0;
1688 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1689 if (!indices) {
1690 SkDebugf("Could not allocate indices\n");
1691 return;
1692 }
1693
1694 int currStartVertex = 0;
1695 for (const auto& circle : fCircles) {
1696 // The inner radius in the vertex data must be specified in normalized space so that
1697 // length() can be called with smaller values to avoid precision issues with half
1698 // floats.
1699 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1700 const SkRect& bounds = circle.fDevBounds;
1701 bool reflect = false;
1702 SkScalar totalAngle = circle.fTotalAngle;
1703 if (totalAngle < 0) {
1704 reflect = true;
1705 totalAngle = -totalAngle;
1706 }
1707
1708 // The bounding geometry for the circle is composed of an outer bounding octagon and
1709 // an inner bounded octagon.
1710
1711 // Initializes the attributes that are the same at each vertex. Also applies reflection.
1712 auto init_const_attrs_and_reflect = [&](CircleVertex* v) {
1713 v->fColor = circle.fColor;
1714 v->fOuterRadius = circle.fOuterRadius;
1715 v->fInnerRadius = normInnerRadius;
1716 v->fOnAngle = circle.fOnAngle;
1717 v->fTotalAngle = totalAngle;
1718 v->fStartAngle = circle.fStartAngle;
1719 v->fPhaseAngle = circle.fPhaseAngle;
1720 if (reflect) {
1721 v->fStartAngle = -v->fStartAngle;
1722 v->fOffset.fY = -v->fOffset.fY;
1723 }
1724 };
1725
1726 // Compute the vertices of the outer octagon.
1727 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1728 SkScalar halfWidth = 0.5f * bounds.width();
1729 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
Brian Salomon92be2f72018-06-19 14:33:47 -04001730 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * kVertexStride);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001731 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
1732 v->fOffset = {x, y};
1733 init_const_attrs_and_reflect(v);
1734 };
1735 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
1736 init_outer_vertex(0, -kOctOffset, -1);
1737 init_outer_vertex(1, kOctOffset, -1);
1738 init_outer_vertex(2, 1, -kOctOffset);
1739 init_outer_vertex(3, 1, kOctOffset);
1740 init_outer_vertex(4, kOctOffset, 1);
1741 init_outer_vertex(5, -kOctOffset, 1);
1742 init_outer_vertex(6, -1, kOctOffset);
1743 init_outer_vertex(7, -1, -kOctOffset);
1744
1745 // Compute the vertices of the inner octagon.
1746 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
1747 CircleVertex* v =
Brian Salomon92be2f72018-06-19 14:33:47 -04001748 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * kVertexStride);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001749 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
1750 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
1751 init_const_attrs_and_reflect(v);
1752 };
1753
1754 // cosine and sine of pi/8
1755 static constexpr SkScalar kCos = 0.923579533f;
1756 static constexpr SkScalar kSin = 0.382683432f;
1757
1758 init_inner_vertex(0, -kSin, -kCos);
1759 init_inner_vertex(1, kSin, -kCos);
1760 init_inner_vertex(2, kCos, -kSin);
1761 init_inner_vertex(3, kCos, kSin);
1762 init_inner_vertex(4, kSin, kCos);
1763 init_inner_vertex(5, -kSin, kCos);
1764 init_inner_vertex(6, -kCos, kSin);
1765 init_inner_vertex(7, -kCos, -kSin);
1766
1767 const uint16_t* primIndices = circle_type_to_indices(true);
1768 const int primIndexCount = circle_type_to_index_count(true);
1769 for (int i = 0; i < primIndexCount; ++i) {
1770 *indices++ = primIndices[i] + currStartVertex;
1771 }
1772
1773 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon92be2f72018-06-19 14:33:47 -04001774 vertices += circle_type_to_vert_count(true) * kVertexStride;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001775 }
1776
1777 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04001778 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1779 GrPrimitiveRestart::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001780 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001781 auto pipe = fHelper.makePipeline(target);
1782 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001783 }
1784
1785 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1786 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1787
1788 // can only represent 65535 unique vertices with 16-bit indices
1789 if (fVertCount + that->fVertCount > 65536) {
1790 return false;
1791 }
1792
1793 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1794 return false;
1795 }
1796
1797 if (fHelper.usesLocalCoords() &&
1798 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1799 return false;
1800 }
1801
1802 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1803 this->joinBounds(*that);
1804 fVertCount += that->fVertCount;
1805 fIndexCount += that->fIndexCount;
1806 return true;
1807 }
1808
1809 struct Circle {
1810 GrColor fColor;
1811 SkScalar fOuterRadius;
1812 SkScalar fInnerRadius;
1813 SkScalar fOnAngle;
1814 SkScalar fTotalAngle;
1815 SkScalar fStartAngle;
1816 SkScalar fPhaseAngle;
1817 SkRect fDevBounds;
1818 };
1819
1820 SkMatrix fViewMatrixIfUsingLocalCoords;
1821 Helper fHelper;
1822 SkSTArray<1, Circle, true> fCircles;
1823 int fVertCount;
1824 int fIndexCount;
1825
1826 typedef GrMeshDrawOp INHERITED;
1827};
1828
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001829///////////////////////////////////////////////////////////////////////////////
1830
Brian Salomon05441c42017-05-15 16:45:49 -04001831class EllipseOp : public GrMeshDrawOp {
1832private:
1833 using Helper = GrSimpleMeshDrawOpHelper;
1834
1835 struct DeviceSpaceParams {
1836 SkPoint fCenter;
1837 SkScalar fXRadius;
1838 SkScalar fYRadius;
1839 SkScalar fInnerXRadius;
1840 SkScalar fInnerYRadius;
1841 };
1842
joshualitt76e7fb62015-02-11 08:52:27 -08001843public:
Brian Salomon25a88092016-12-01 09:36:50 -05001844 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001845
Robert Phillips7c525e62018-06-12 10:11:12 -04001846 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1847 GrPaint&& paint,
1848 const SkMatrix& viewMatrix,
1849 const SkRect& ellipse,
1850 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001851 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001852 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001853 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1854 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001855 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1856 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001857 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1858 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1859 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1860 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001861
bsalomon4b4a7cc2016-07-08 04:42:54 -07001862 // do (potentially) anisotropic mapping of stroke
1863 SkVector scaledStroke;
1864 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001865 scaledStroke.fX = SkScalarAbs(
1866 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1867 scaledStroke.fY = SkScalarAbs(
1868 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869
1870 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001871 bool isStrokeOnly =
1872 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001873 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1874
Brian Salomon05441c42017-05-15 16:45:49 -04001875 params.fInnerXRadius = 0;
1876 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001877 if (hasStroke) {
1878 if (SkScalarNearlyZero(scaledStroke.length())) {
1879 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1880 } else {
1881 scaledStroke.scale(SK_ScalarHalf);
1882 }
1883
1884 // we only handle thick strokes for near-circular ellipses
1885 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001886 (0.5f * params.fXRadius > params.fYRadius ||
1887 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001888 return nullptr;
1889 }
1890
1891 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001892 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1893 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1894 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1895 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001896 return nullptr;
1897 }
1898
1899 // this is legit only if scale & translation (which should be the case at the moment)
1900 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001901 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1902 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001903 }
1904
Brian Salomon05441c42017-05-15 16:45:49 -04001905 params.fXRadius += scaledStroke.fX;
1906 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001907 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001908 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
1909 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001910 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001911
Brian Salomonea26d6b2018-01-23 20:33:21 +00001912 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1913 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1914 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001915 SkStrokeRec::Style style = stroke.getStyle();
1916 bool isStrokeOnly =
1917 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001918
Brian Salomon05441c42017-05-15 16:45:49 -04001919 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1920 params.fInnerXRadius, params.fInnerYRadius,
1921 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1922 params.fCenter.fY - params.fYRadius,
1923 params.fCenter.fX + params.fXRadius,
1924 params.fCenter.fY + params.fYRadius)});
1925
1926 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001927
bsalomon4b4a7cc2016-07-08 04:42:54 -07001928 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001929 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001930
Brian Salomon05441c42017-05-15 16:45:49 -04001931 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1932 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001933 }
joshualitt76e7fb62015-02-11 08:52:27 -08001934
Brian Salomon289e3d82016-12-14 15:52:56 -05001935 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001936
Robert Phillipsf1748f52017-09-14 14:11:24 -04001937 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001938 fHelper.visitProxies(func);
1939 }
1940
Brian Salomon7c3e7182016-12-01 09:35:30 -05001941 SkString dumpInfo() const override {
1942 SkString string;
1943 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001944 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001945 string.appendf(
1946 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1947 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1948 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1949 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1950 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001951 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001952 string += fHelper.dumpInfo();
1953 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001954 return string;
1955 }
1956
Brian Osman9a725dd2017-09-20 09:53:22 -04001957 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1958 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001959 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001960 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1961 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001962 }
1963
1964 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1965
bsalomone46f9fe2015-08-18 06:05:14 -07001966private:
Brian Salomon91326c32017-08-09 16:02:19 -04001967 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001968 SkMatrix localMatrix;
1969 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001970 return;
1971 }
1972
1973 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00001974 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001975
bsalomonb5238a72015-05-05 07:49:49 -07001976 QuadHelper helper;
Brian Salomon92be2f72018-06-19 14:33:47 -04001977 SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
Brian Salomon05441c42017-05-15 16:45:49 -04001978 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04001979 helper.init(target, sizeof(EllipseVertex), fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001980 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001981 return;
1982 }
1983
Brian Salomon05441c42017-05-15 16:45:49 -04001984 for (const auto& ellipse : fEllipses) {
1985 GrColor color = ellipse.fColor;
1986 SkScalar xRadius = ellipse.fXRadius;
1987 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001988
1989 // Compute the reciprocals of the radii here to save time in the shader
1990 SkScalar xRadRecip = SkScalarInvert(xRadius);
1991 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001992 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1993 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
vjiaoblack977996d2016-06-30 12:20:54 -07001994 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1995 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1996
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001997 if (!fStroked) {
1998 // For filled ellipses we map a unit circle in the vertex attributes rather than
1999 // computing an ellipse and modifying that distance, so we normalize to 1
2000 xMaxOffset /= xRadius;
2001 yMaxOffset /= yRadius;
2002 }
2003
joshualitt76e7fb62015-02-11 08:52:27 -08002004 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04002005 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002006 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07002007 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002008 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2009 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2010
Brian Salomon05441c42017-05-15 16:45:49 -04002011 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002012 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07002013 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002014 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2015 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2016
Brian Salomon57caa662017-10-18 12:21:05 +00002017 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002018 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002019 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002020 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2021 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2022
Brian Salomon57caa662017-10-18 12:21:05 +00002023 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002024 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002025 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002026 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2027 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2028
bsalomonb5238a72015-05-05 07:49:49 -07002029 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002030 }
Brian Salomon49348902018-06-26 09:12:38 -04002031 auto pipe = fHelper.makePipeline(target);
2032 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002033 }
2034
Brian Salomon25a88092016-12-01 09:36:50 -05002035 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002036 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002037
Brian Salomon05441c42017-05-15 16:45:49 -04002038 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002039 return false;
2040 }
2041
bsalomoncdaa97b2016-03-08 08:30:14 -08002042 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002043 return false;
2044 }
2045
Brian Salomon05441c42017-05-15 16:45:49 -04002046 if (fHelper.usesLocalCoords() &&
2047 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002048 return false;
2049 }
2050
Brian Salomon05441c42017-05-15 16:45:49 -04002051 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002052 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002053 return true;
2054 }
2055
Brian Salomon05441c42017-05-15 16:45:49 -04002056 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002057 GrColor fColor;
2058 SkScalar fXRadius;
2059 SkScalar fYRadius;
2060 SkScalar fInnerXRadius;
2061 SkScalar fInnerYRadius;
2062 SkRect fDevBounds;
2063 };
joshualitt76e7fb62015-02-11 08:52:27 -08002064
Brian Salomon289e3d82016-12-14 15:52:56 -05002065 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002066 Helper fHelper;
2067 bool fStroked;
2068 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002069
Brian Salomon05441c42017-05-15 16:45:49 -04002070 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002071};
2072
joshualitt76e7fb62015-02-11 08:52:27 -08002073/////////////////////////////////////////////////////////////////////////////////////////////////
2074
Brian Salomon05441c42017-05-15 16:45:49 -04002075class DIEllipseOp : public GrMeshDrawOp {
2076private:
2077 using Helper = GrSimpleMeshDrawOpHelper;
2078
2079 struct DeviceSpaceParams {
2080 SkPoint fCenter;
2081 SkScalar fXRadius;
2082 SkScalar fYRadius;
2083 SkScalar fInnerXRadius;
2084 SkScalar fInnerYRadius;
2085 DIEllipseStyle fStyle;
2086 };
2087
joshualitt76e7fb62015-02-11 08:52:27 -08002088public:
Brian Salomon25a88092016-12-01 09:36:50 -05002089 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002090
Robert Phillips7c525e62018-06-12 10:11:12 -04002091 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2092 GrPaint&& paint,
2093 const SkMatrix& viewMatrix,
2094 const SkRect& ellipse,
2095 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002096 DeviceSpaceParams params;
2097 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2098 params.fXRadius = SkScalarHalf(ellipse.width());
2099 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002100
bsalomon4b4a7cc2016-07-08 04:42:54 -07002101 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002102 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2103 ? DIEllipseStyle::kStroke
2104 : (SkStrokeRec::kHairline_Style == style)
2105 ? DIEllipseStyle::kHairline
2106 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002107
Brian Salomon05441c42017-05-15 16:45:49 -04002108 params.fInnerXRadius = 0;
2109 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002110 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2111 SkScalar strokeWidth = stroke.getWidth();
2112
2113 if (SkScalarNearlyZero(strokeWidth)) {
2114 strokeWidth = SK_ScalarHalf;
2115 } else {
2116 strokeWidth *= SK_ScalarHalf;
2117 }
2118
2119 // we only handle thick strokes for near-circular ellipses
2120 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002121 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2122 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002123 return nullptr;
2124 }
2125
2126 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002127 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2128 (strokeWidth * strokeWidth) * params.fXRadius) {
2129 return nullptr;
2130 }
2131 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2132 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002133 return nullptr;
2134 }
2135
2136 // set inner radius (if needed)
2137 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002138 params.fInnerXRadius = params.fXRadius - strokeWidth;
2139 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002140 }
2141
Brian Salomon05441c42017-05-15 16:45:49 -04002142 params.fXRadius += strokeWidth;
2143 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002144 }
Brian Salomon05441c42017-05-15 16:45:49 -04002145 if (DIEllipseStyle::kStroke == params.fStyle &&
2146 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2147 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002148 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002149 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002150 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002151
Brian Salomonea26d6b2018-01-23 20:33:21 +00002152 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
2153 const SkMatrix& viewMatrix)
2154 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002155 // This expands the outer rect so that after CTM we end up with a half-pixel border
2156 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2157 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2158 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2159 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05002160 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2161 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002162
Brian Salomon05441c42017-05-15 16:45:49 -04002163 fEllipses.emplace_back(
2164 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2165 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2166 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2167 params.fCenter.fY - params.fYRadius - geoDy,
2168 params.fCenter.fX + params.fXRadius + geoDx,
2169 params.fCenter.fY + params.fYRadius + geoDy)});
2170 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2171 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002172 }
2173
Brian Salomon289e3d82016-12-14 15:52:56 -05002174 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002175
Robert Phillipsf1748f52017-09-14 14:11:24 -04002176 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002177 fHelper.visitProxies(func);
2178 }
2179
Brian Salomon7c3e7182016-12-01 09:35:30 -05002180 SkString dumpInfo() const override {
2181 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002182 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002183 string.appendf(
2184 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2185 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2186 "GeoDY: %.2f\n",
2187 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
2188 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2189 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002190 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002191 string += fHelper.dumpInfo();
2192 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002193 return string;
2194 }
2195
Brian Osman9a725dd2017-09-20 09:53:22 -04002196 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2197 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002198 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002199 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2200 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002201 }
2202
2203 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2204
bsalomone46f9fe2015-08-18 06:05:14 -07002205private:
Brian Salomon91326c32017-08-09 16:02:19 -04002206 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002207 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002208 sk_sp<GrGeometryProcessor> gp(
Brian Salomonea26d6b2018-01-23 20:33:21 +00002209 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002210
Brian Salomon92be2f72018-06-19 14:33:47 -04002211 SkASSERT(sizeof(DIEllipseVertex) == gp->debugOnly_vertexStride());
bsalomonb5238a72015-05-05 07:49:49 -07002212 QuadHelper helper;
2213 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04002214 helper.init(target, sizeof(DIEllipseVertex), fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002215 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002216 return;
2217 }
2218
Brian Salomon05441c42017-05-15 16:45:49 -04002219 for (const auto& ellipse : fEllipses) {
2220 GrColor color = ellipse.fColor;
2221 SkScalar xRadius = ellipse.fXRadius;
2222 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002223
Brian Salomon05441c42017-05-15 16:45:49 -04002224 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002225
2226 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002227 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2228 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002229
joshualitt76e7fb62015-02-11 08:52:27 -08002230 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002231 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002232 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002233 verts[0].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002234
Brian Salomon289e3d82016-12-14 15:52:56 -05002235 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002236 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002237 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002238 verts[1].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002239
Brian Salomon57caa662017-10-18 12:21:05 +00002240 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002241 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002242 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002243 verts[2].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002244
Brian Salomon57caa662017-10-18 12:21:05 +00002245 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002246 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002247 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002248 verts[3].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
2249
2250 if (DIEllipseStyle::kStroke == this->style()) {
2251 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
2252 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
2253
2254 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2255 -innerRatioY - offsetDy);
2256 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2257 innerRatioY + offsetDy);
2258 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2259 -innerRatioY - offsetDy);
2260 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2261 innerRatioY + offsetDy);
2262 }
joshualitt76e7fb62015-02-11 08:52:27 -08002263
bsalomonb5238a72015-05-05 07:49:49 -07002264 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002265 }
Brian Salomon49348902018-06-26 09:12:38 -04002266 auto pipe = fHelper.makePipeline(target);
2267 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002268 }
halcanary9d524f22016-03-29 09:03:52 -07002269
Brian Salomon25a88092016-12-01 09:36:50 -05002270 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002271 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002272 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002273 return false;
2274 }
2275
bsalomoncdaa97b2016-03-08 08:30:14 -08002276 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08002277 return false;
2278 }
2279
joshualittd96a67b2015-05-05 14:09:05 -07002280 // TODO rewrite to allow positioning on CPU
2281 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08002282 return false;
2283 }
2284
Brian Salomon05441c42017-05-15 16:45:49 -04002285 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002286 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002287 return true;
2288 }
2289
Brian Salomon05441c42017-05-15 16:45:49 -04002290 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2291 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002292
Brian Salomon05441c42017-05-15 16:45:49 -04002293 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002294 SkMatrix fViewMatrix;
2295 GrColor fColor;
2296 SkScalar fXRadius;
2297 SkScalar fYRadius;
2298 SkScalar fInnerXRadius;
2299 SkScalar fInnerYRadius;
2300 SkScalar fGeoDx;
2301 SkScalar fGeoDy;
2302 DIEllipseStyle fStyle;
2303 SkRect fBounds;
2304 };
2305
Brian Salomon05441c42017-05-15 16:45:49 -04002306 Helper fHelper;
2307 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002308
Brian Salomon05441c42017-05-15 16:45:49 -04002309 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002310};
2311
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002312///////////////////////////////////////////////////////////////////////////////
2313
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002314// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002315//
2316// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2317// ____________
2318// |_|________|_|
2319// | | | |
2320// | | | |
2321// | | | |
2322// |_|________|_|
2323// |_|________|_|
2324//
2325// For strokes, we don't draw the center quad.
2326//
2327// For circular roundrects, in the case where the stroke width is greater than twice
2328// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002329// in the center. The shared vertices are duplicated so we can set a different outer radius
2330// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002331// ____________
2332// |_|________|_|
2333// | |\ ____ /| |
2334// | | | | | |
2335// | | |____| | |
2336// |_|/______\|_|
2337// |_|________|_|
2338//
2339// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002340//
2341// For filled rrects that need to provide a distance vector we resuse the overstroke
2342// geometry but make the inner rect degenerate (either a point or a horizontal or
2343// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002344
jvanverth84839f62016-08-29 10:16:40 -07002345static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002346 // clang-format off
2347 // overstroke quads
2348 // we place this at the beginning so that we can skip these indices when rendering normally
2349 16, 17, 19, 16, 19, 18,
2350 19, 17, 23, 19, 23, 21,
2351 21, 23, 22, 21, 22, 20,
2352 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002353
Brian Salomon289e3d82016-12-14 15:52:56 -05002354 // corners
2355 0, 1, 5, 0, 5, 4,
2356 2, 3, 7, 2, 7, 6,
2357 8, 9, 13, 8, 13, 12,
2358 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002359
Brian Salomon289e3d82016-12-14 15:52:56 -05002360 // edges
2361 1, 2, 6, 1, 6, 5,
2362 4, 5, 9, 4, 9, 8,
2363 6, 7, 11, 6, 11, 10,
2364 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002365
Brian Salomon289e3d82016-12-14 15:52:56 -05002366 // center
2367 // we place this at the end so that we can ignore these indices when not rendering as filled
2368 5, 6, 10, 5, 10, 9,
2369 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002370};
Brian Salomon289e3d82016-12-14 15:52:56 -05002371
jvanverth84839f62016-08-29 10:16:40 -07002372// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002373static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002374
jvanverth84839f62016-08-29 10:16:40 -07002375// overstroke count is arraysize minus the center indices
2376static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2377// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002378static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002379// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002380static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2381static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002382static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002383
jvanverthc3d0e422016-08-25 08:12:35 -07002384enum RRectType {
2385 kFill_RRectType,
2386 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002387 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002388};
2389
jvanverth84839f62016-08-29 10:16:40 -07002390static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002391 switch (type) {
2392 case kFill_RRectType:
2393 case kStroke_RRectType:
2394 return kVertsPerStandardRRect;
2395 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002396 return kVertsPerOverstrokeRRect;
2397 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002398 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002399 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002400}
2401
2402static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002403 switch (type) {
2404 case kFill_RRectType:
2405 return kIndicesPerFillRRect;
2406 case kStroke_RRectType:
2407 return kIndicesPerStrokeRRect;
2408 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002409 return kIndicesPerOverstrokeRRect;
2410 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002411 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002412 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002413}
2414
2415static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002416 switch (type) {
2417 case kFill_RRectType:
2418 case kStroke_RRectType:
2419 return gStandardRRectIndices;
2420 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002421 return gOverstrokeRRectIndices;
2422 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002423 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002424 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002425}
2426
joshualitt76e7fb62015-02-11 08:52:27 -08002427///////////////////////////////////////////////////////////////////////////////////////////////////
2428
Robert Phillips79839d42016-10-06 15:03:34 -04002429// For distance computations in the interior of filled rrects we:
2430//
2431// add a interior degenerate (point or line) rect
2432// each vertex of that rect gets -outerRad as its radius
2433// this makes the computation of the distance to the outer edge be negative
2434// negative values are caught and then handled differently in the GP's onEmitCode
2435// each vertex is also given the normalized x & y distance from the interior rect's edge
2436// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2437
Brian Salomon05441c42017-05-15 16:45:49 -04002438class CircularRRectOp : public GrMeshDrawOp {
2439private:
2440 using Helper = GrSimpleMeshDrawOpHelper;
2441
joshualitt76e7fb62015-02-11 08:52:27 -08002442public:
Brian Salomon25a88092016-12-01 09:36:50 -05002443 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002444
bsalomon4b4a7cc2016-07-08 04:42:54 -07002445 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2446 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002447 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2448 GrPaint&& paint,
2449 const SkMatrix& viewMatrix,
2450 const SkRect& devRect,
2451 float devRadius,
2452 float devStrokeWidth,
2453 bool strokeOnly) {
2454 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
2455 devRect, devRadius,
2456 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002457 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002458 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
2459 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002460 : INHERITED(ClassID())
2461 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002462 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002463 SkRect bounds = devRect;
2464 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2465 SkScalar innerRadius = 0.0f;
2466 SkScalar outerRadius = devRadius;
2467 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002468 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002469 if (devStrokeWidth > 0) {
2470 if (SkScalarNearlyZero(devStrokeWidth)) {
2471 halfWidth = SK_ScalarHalf;
2472 } else {
2473 halfWidth = SkScalarHalf(devStrokeWidth);
2474 }
joshualitt76e7fb62015-02-11 08:52:27 -08002475
bsalomon4b4a7cc2016-07-08 04:42:54 -07002476 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002477 // Outset stroke by 1/4 pixel
2478 devStrokeWidth += 0.25f;
2479 // If stroke is greater than width or height, this is still a fill
2480 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002481 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002482 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002483 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002484 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002485 }
2486 outerRadius += halfWidth;
2487 bounds.outset(halfWidth, halfWidth);
2488 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002489
bsalomon4b4a7cc2016-07-08 04:42:54 -07002490 // The radii are outset for two reasons. First, it allows the shader to simply perform
2491 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2492 // Second, the outer radius is used to compute the verts of the bounding box that is
2493 // rendered and the outset ensures the box will cover all partially covered by the rrect
2494 // corners.
2495 outerRadius += SK_ScalarHalf;
2496 innerRadius -= SK_ScalarHalf;
2497
bsalomon88cf17d2016-07-08 06:40:56 -07002498 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2499
2500 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002501 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2502
Brian Salomon05441c42017-05-15 16:45:49 -04002503 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002504 fVertCount = rrect_type_to_vert_count(type);
2505 fIndexCount = rrect_type_to_index_count(type);
2506 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002507 }
2508
Brian Salomon289e3d82016-12-14 15:52:56 -05002509 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002510
Robert Phillipsf1748f52017-09-14 14:11:24 -04002511 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002512 fHelper.visitProxies(func);
2513 }
2514
jvanverthc3d0e422016-08-25 08:12:35 -07002515 SkString dumpInfo() const override {
2516 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002517 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002518 string.appendf(
2519 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2520 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04002521 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
2522 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
2523 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002524 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002525 string += fHelper.dumpInfo();
2526 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002527 return string;
2528 }
2529
Brian Osman9a725dd2017-09-20 09:53:22 -04002530 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2531 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002532 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002533 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2534 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002535 }
2536
2537 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2538
Brian Salomon92aee3d2016-12-21 09:20:25 -05002539private:
Robert Phillips79839d42016-10-06 15:03:34 -04002540 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05002541 SkPoint fPos;
2542 GrColor fColor;
2543 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04002544 SkScalar fOuterRadius;
2545 SkScalar fInnerRadius;
2546 // No half plane, we don't use it here.
2547 };
2548
Brian Salomon289e3d82016-12-14 15:52:56 -05002549 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
2550 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2551 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002552 SkASSERT(smInset < bigInset);
2553
2554 // TL
2555 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
2556 (*verts)->fColor = color;
2557 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2558 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002559 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002560 (*verts)++;
2561
2562 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05002563 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002564 (*verts)->fColor = color;
2565 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2566 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002567 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002568 (*verts)++;
2569
Brian Salomon289e3d82016-12-14 15:52:56 -05002570 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002571 (*verts)->fColor = color;
2572 (*verts)->fOffset = SkPoint::Make(0, 0);
2573 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002574 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002575 (*verts)++;
2576
Brian Salomon289e3d82016-12-14 15:52:56 -05002577 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002578 (*verts)->fColor = color;
2579 (*verts)->fOffset = SkPoint::Make(0, 0);
2580 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002581 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002582 (*verts)++;
2583
2584 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
2585 (*verts)->fColor = color;
2586 (*verts)->fOffset = SkPoint::Make(0, 0);
2587 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002588 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002589 (*verts)++;
2590
2591 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
2592 (*verts)->fColor = color;
2593 (*verts)->fOffset = SkPoint::Make(0, 0);
2594 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002595 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002596 (*verts)++;
2597
2598 // BL
2599 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
2600 (*verts)->fColor = color;
2601 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2602 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002603 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002604 (*verts)++;
2605
2606 // BR
2607 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
2608 (*verts)->fColor = color;
2609 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2610 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002611 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002612 (*verts)++;
2613 }
2614
Brian Salomon91326c32017-08-09 16:02:19 -04002615 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002616 // Invert the view matrix as a local matrix (if any other processors require coords).
2617 SkMatrix localMatrix;
2618 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002619 return;
2620 }
2621
2622 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002623 sk_sp<GrGeometryProcessor> gp(
Brian Salomon45c92202018-04-10 10:53:58 -04002624 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002625
Brian Salomon92be2f72018-06-19 14:33:47 -04002626 SkASSERT(sizeof(CircleVertex) == gp->debugOnly_vertexStride());
joshualitt76e7fb62015-02-11 08:52:27 -08002627
jvanverth84839f62016-08-29 10:16:40 -07002628 const GrBuffer* vertexBuffer;
2629 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002630
Brian Salomon92be2f72018-06-19 14:33:47 -04002631 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
2632 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07002633 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002634 SkDebugf("Could not allocate vertices\n");
2635 return;
2636 }
2637
jvanverth84839f62016-08-29 10:16:40 -07002638 const GrBuffer* indexBuffer = nullptr;
2639 int firstIndex = 0;
2640 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2641 if (!indices) {
2642 SkDebugf("Could not allocate indices\n");
2643 return;
2644 }
2645
2646 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002647 for (const auto& rrect : fRRects) {
2648 GrColor color = rrect.fColor;
2649 SkScalar outerRadius = rrect.fOuterRadius;
2650 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002651
Brian Salomon289e3d82016-12-14 15:52:56 -05002652 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2653 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002654
Brian Salomon289e3d82016-12-14 15:52:56 -05002655 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002656 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002657 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002658 SkScalar innerRadius = rrect.fType != kFill_RRectType
2659 ? rrect.fInnerRadius / rrect.fOuterRadius
2660 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002661 for (int i = 0; i < 4; ++i) {
2662 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002663 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002664 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
2665 verts->fOuterRadius = outerRadius;
2666 verts->fInnerRadius = innerRadius;
2667 verts++;
2668
2669 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002670 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002671 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2672 verts->fOuterRadius = outerRadius;
2673 verts->fInnerRadius = innerRadius;
2674 verts++;
2675
2676 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002677 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002678 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2679 verts->fOuterRadius = outerRadius;
2680 verts->fInnerRadius = innerRadius;
2681 verts++;
2682
2683 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002684 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002685 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
2686 verts->fOuterRadius = outerRadius;
2687 verts->fInnerRadius = innerRadius;
2688 verts++;
2689 }
jvanverthc3d0e422016-08-25 08:12:35 -07002690 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002691 // Effectively this is an additional stroked rrect, with its
2692 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2693 // This will give us correct AA in the center and the correct
2694 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002695 //
jvanvertha4f1af82016-08-29 07:17:47 -07002696 // Also, the outer offset is a constant vector pointing to the right, which
2697 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002698 if (kOverstroke_RRectType == rrect.fType) {
2699 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002700
Brian Salomon05441c42017-05-15 16:45:49 -04002701 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002702 // this is the normalized distance from the outer rectangle of this
2703 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002704 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002705
Brian Salomon289e3d82016-12-14 15:52:56 -05002706 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002707 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002708 }
jvanverth6a397612016-08-26 08:15:33 -07002709
Brian Salomon05441c42017-05-15 16:45:49 -04002710 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2711 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002712 for (int i = 0; i < primIndexCount; ++i) {
2713 *indices++ = primIndices[i] + currStartVertex;
2714 }
2715
Brian Salomon05441c42017-05-15 16:45:49 -04002716 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002717 }
2718
Chris Dalton3809bab2017-06-13 10:55:06 -06002719 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04002720 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
2721 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06002722 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04002723 auto pipe = fHelper.makePipeline(target);
2724 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002725 }
2726
Brian Salomon25a88092016-12-01 09:36:50 -05002727 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002728 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002729
2730 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002731 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002732 return false;
2733 }
2734
Brian Salomon05441c42017-05-15 16:45:49 -04002735 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002736 return false;
2737 }
2738
Brian Salomon05441c42017-05-15 16:45:49 -04002739 if (fHelper.usesLocalCoords() &&
2740 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002741 return false;
2742 }
2743
Brian Salomon05441c42017-05-15 16:45:49 -04002744 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002745 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002746 fVertCount += that->fVertCount;
2747 fIndexCount += that->fIndexCount;
2748 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002749 return true;
2750 }
2751
Brian Salomon05441c42017-05-15 16:45:49 -04002752 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002753 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002754 SkScalar fInnerRadius;
2755 SkScalar fOuterRadius;
2756 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002757 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002758 };
2759
Brian Salomon289e3d82016-12-14 15:52:56 -05002760 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002761 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002762 int fVertCount;
2763 int fIndexCount;
2764 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002765 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002766
Brian Salomon05441c42017-05-15 16:45:49 -04002767 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002768};
2769
jvanverth84839f62016-08-29 10:16:40 -07002770static const int kNumRRectsInIndexBuffer = 256;
2771
2772GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2773GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002774static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2775 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002776 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2777 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2778 switch (type) {
2779 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002780 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002781 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2782 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002783 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002784 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002785 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2786 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002787 default:
2788 SkASSERT(false);
2789 return nullptr;
2790 };
2791}
2792
Brian Salomon05441c42017-05-15 16:45:49 -04002793class EllipticalRRectOp : public GrMeshDrawOp {
2794private:
2795 using Helper = GrSimpleMeshDrawOpHelper;
2796
joshualitt76e7fb62015-02-11 08:52:27 -08002797public:
Brian Salomon25a88092016-12-01 09:36:50 -05002798 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002799
bsalomon4b4a7cc2016-07-08 04:42:54 -07002800 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2801 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002802 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2803 GrPaint&& paint,
2804 const SkMatrix& viewMatrix,
2805 const SkRect& devRect,
2806 float devXRadius,
2807 float devYRadius,
2808 SkVector devStrokeWidths,
2809 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002810 SkASSERT(devXRadius > 0.5);
2811 SkASSERT(devYRadius > 0.5);
2812 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2813 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002814 if (devStrokeWidths.fX > 0) {
2815 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2816 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2817 } else {
2818 devStrokeWidths.scale(SK_ScalarHalf);
2819 }
joshualitt76e7fb62015-02-11 08:52:27 -08002820
bsalomon4b4a7cc2016-07-08 04:42:54 -07002821 // we only handle thick strokes for near-circular ellipses
2822 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002823 (SK_ScalarHalf * devXRadius > devYRadius ||
2824 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002825 return nullptr;
2826 }
2827
2828 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002829 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2830 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002831 return nullptr;
2832 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002833 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2834 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002835 return nullptr;
2836 }
Brian Salomon05441c42017-05-15 16:45:49 -04002837 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002838 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
2839 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002840 devXRadius, devYRadius, devStrokeWidths,
2841 strokeOnly);
2842 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002843
Brian Salomonea26d6b2018-01-23 20:33:21 +00002844 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2845 const SkRect& devRect, float devXRadius, float devYRadius,
2846 SkVector devStrokeHalfWidths, bool strokeOnly)
2847 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002848 SkScalar innerXRadius = 0.0f;
2849 SkScalar innerYRadius = 0.0f;
2850 SkRect bounds = devRect;
2851 bool stroked = false;
2852 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002853 // this is legit only if scale & translation (which should be the case at the moment)
2854 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002855 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2856 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002857 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2858 }
2859
Brian Salomon05441c42017-05-15 16:45:49 -04002860 devXRadius += devStrokeHalfWidths.fX;
2861 devYRadius += devStrokeHalfWidths.fY;
2862 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002863 }
2864
Brian Salomon05441c42017-05-15 16:45:49 -04002865 fStroked = stroked;
2866 fViewMatrixIfUsingLocalCoords = viewMatrix;
2867 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002868 // Expand the rect for aa in order to generate the correct vertices.
2869 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002870 fRRects.emplace_back(
2871 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002872 }
2873
Brian Salomon289e3d82016-12-14 15:52:56 -05002874 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002875
Robert Phillipsf1748f52017-09-14 14:11:24 -04002876 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002877 fHelper.visitProxies(func);
2878 }
2879
Brian Salomon7c3e7182016-12-01 09:35:30 -05002880 SkString dumpInfo() const override {
2881 SkString string;
2882 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002883 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002884 string.appendf(
2885 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2886 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2887 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2888 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2889 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002890 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002891 string += fHelper.dumpInfo();
2892 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002893 return string;
2894 }
2895
Brian Osman9a725dd2017-09-20 09:53:22 -04002896 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2897 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002898 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002899 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2900 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002901 }
2902
2903 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2904
bsalomone46f9fe2015-08-18 06:05:14 -07002905private:
Brian Salomon91326c32017-08-09 16:02:19 -04002906 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002907 SkMatrix localMatrix;
2908 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002909 return;
2910 }
2911
2912 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00002913 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002914
Brian Salomon92be2f72018-06-19 14:33:47 -04002915 SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
joshualitt76e7fb62015-02-11 08:52:27 -08002916
bsalomonb5238a72015-05-05 07:49:49 -07002917 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002918 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002919 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2920 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002921
Chris Dalton3809bab2017-06-13 10:55:06 -06002922 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002923 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04002924 helper.init(target, sizeof(EllipseVertex), indexBuffer.get(),
2925 kVertsPerStandardRRect, indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002926 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002927 SkDebugf("Could not allocate vertices\n");
2928 return;
2929 }
2930
Brian Salomon05441c42017-05-15 16:45:49 -04002931 for (const auto& rrect : fRRects) {
2932 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002933 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002934 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2935 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2936 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2937 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002938
2939 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002940 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2941 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002942
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002943 SkScalar xMaxOffset = xOuterRadius;
2944 SkScalar yMaxOffset = yOuterRadius;
2945 if (!fStroked) {
2946 // For filled rrects we map a unit circle in the vertex attributes rather than
2947 // computing an ellipse and modifying that distance, so we normalize to 1.
2948 xMaxOffset /= rrect.fXRadius;
2949 yMaxOffset /= rrect.fYRadius;
2950 }
2951
Brian Salomon05441c42017-05-15 16:45:49 -04002952 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002953
Brian Salomon289e3d82016-12-14 15:52:56 -05002954 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2955 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002956 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002957 SK_ScalarNearlyZero, // we're using inversesqrt() in
2958 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002959 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08002960
2961 for (int i = 0; i < 4; ++i) {
2962 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002963 verts->fColor = color;
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002964 verts->fOffset = SkPoint::Make(xMaxOffset, yOuterOffsets[i]);
joshualitt76e7fb62015-02-11 08:52:27 -08002965 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2966 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2967 verts++;
2968
2969 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002970 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002971 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2972 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2973 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2974 verts++;
2975
2976 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002977 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002978 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2979 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2980 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2981 verts++;
2982
2983 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002984 verts->fColor = color;
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002985 verts->fOffset = SkPoint::Make(xMaxOffset, yOuterOffsets[i]);
joshualitt76e7fb62015-02-11 08:52:27 -08002986 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2987 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2988 verts++;
2989 }
2990 }
Brian Salomon49348902018-06-26 09:12:38 -04002991 auto pipe = fHelper.makePipeline(target);
2992 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002993 }
2994
Brian Salomon25a88092016-12-01 09:36:50 -05002995 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002996 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002997
Brian Salomon05441c42017-05-15 16:45:49 -04002998 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002999 return false;
3000 }
3001
bsalomoncdaa97b2016-03-08 08:30:14 -08003002 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08003003 return false;
3004 }
3005
Brian Salomon05441c42017-05-15 16:45:49 -04003006 if (fHelper.usesLocalCoords() &&
3007 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08003008 return false;
3009 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003010
Brian Salomon05441c42017-05-15 16:45:49 -04003011 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07003012 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08003013 return true;
3014 }
3015
Brian Salomon05441c42017-05-15 16:45:49 -04003016 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003017 GrColor fColor;
3018 SkScalar fXRadius;
3019 SkScalar fYRadius;
3020 SkScalar fInnerXRadius;
3021 SkScalar fInnerYRadius;
3022 SkRect fDevBounds;
3023 };
3024
Brian Salomon289e3d82016-12-14 15:52:56 -05003025 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003026 Helper fHelper;
3027 bool fStroked;
3028 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003029
Brian Salomon05441c42017-05-15 16:45:49 -04003030 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003031};
3032
Robert Phillips7c525e62018-06-12 10:11:12 -04003033static std::unique_ptr<GrDrawOp> make_rrect_op(GrContext* context,
3034 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003035 const SkMatrix& viewMatrix,
3036 const SkRRect& rrect,
3037 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003038 SkASSERT(viewMatrix.rectStaysRect());
3039 SkASSERT(rrect.isSimple());
3040 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003041
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003042 // RRect ops only handle simple, but not too simple, rrects.
3043 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003044 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003045 SkRect bounds;
3046 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003047
Mike Reed242135a2018-02-22 13:41:39 -05003048 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003049 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3050 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3051 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3052 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003053
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003054 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003055
bsalomon4b4a7cc2016-07-08 04:42:54 -07003056 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3057 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003058 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003059
Brian Salomon289e3d82016-12-14 15:52:56 -05003060 bool isStrokeOnly =
3061 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003062 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3063
jvanverthc3d0e422016-08-25 08:12:35 -07003064 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003065 if (hasStroke) {
3066 if (SkStrokeRec::kHairline_Style == style) {
3067 scaledStroke.set(1, 1);
3068 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003069 scaledStroke.fX = SkScalarAbs(
3070 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3071 scaledStroke.fY = SkScalarAbs(
3072 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003073 }
3074
jvanverthc3d0e422016-08-25 08:12:35 -07003075 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
3076 // for non-circular rrects, if half of strokewidth is greater than radius,
3077 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05003078 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
3079 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003080 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003081 }
3082 }
3083
3084 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3085 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3086 // patch will have fractional coverage. This only matters when the interior is actually filled.
3087 // We could consider falling back to rect rendering here, since a tiny radius is
3088 // indistinguishable from a square corner.
3089 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003090 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003091 }
3092
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003093 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003094 if (isCircular) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003095 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, xRadius,
3096 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003097 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003098 } else {
Robert Phillips7c525e62018-06-12 10:11:12 -04003099 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3100 xRadius, yRadius, scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003101 }
joshualitt3e708c52015-04-30 13:49:27 -07003102}
3103
Robert Phillips7c525e62018-06-12 10:11:12 -04003104std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrContext* context,
3105 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003106 const SkMatrix& viewMatrix,
3107 const SkRRect& rrect,
3108 const SkStrokeRec& stroke,
3109 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003110 if (rrect.isOval()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003111 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
3112 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003113 }
3114
3115 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003116 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003117 }
3118
Robert Phillips7c525e62018-06-12 10:11:12 -04003119 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003120}
joshualitt3e708c52015-04-30 13:49:27 -07003121
bsalomon4b4a7cc2016-07-08 04:42:54 -07003122///////////////////////////////////////////////////////////////////////////////
3123
Robert Phillips7c525e62018-06-12 10:11:12 -04003124std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrContext* context,
3125 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003126 const SkMatrix& viewMatrix,
3127 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003128 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003129 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003130 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003131 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003132 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3133 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003134 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003135 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003136 if (style.hasNonDashPathEffect()) {
3137 return nullptr;
3138 } else if (style.isDashed()) {
3139 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3140 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3141 return nullptr;
3142 }
3143 auto onInterval = style.dashIntervals()[0];
3144 auto offInterval = style.dashIntervals()[1];
3145 if (offInterval == 0) {
3146 GrStyle strokeStyle(style.strokeRec(), nullptr);
Robert Phillips7c525e62018-06-12 10:11:12 -04003147 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3148 strokeStyle, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003149 } else if (onInterval == 0) {
3150 // There is nothing to draw but we have no way to indicate that here.
3151 return nullptr;
3152 }
3153 auto angularOnInterval = onInterval / r;
3154 auto angularOffInterval = offInterval / r;
3155 auto phaseAngle = style.dashPhase() / r;
3156 // Currently this function doesn't accept ovals with different start angles, though
3157 // it could.
3158 static const SkScalar kStartAngle = 0.f;
Robert Phillips7c525e62018-06-12 10:11:12 -04003159 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003160 style.strokeRec().getWidth(), kStartAngle,
3161 angularOnInterval, angularOffInterval, phaseAngle);
3162 }
Robert Phillips7c525e62018-06-12 10:11:12 -04003163 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003164 }
3165
3166 if (style.pathEffect()) {
3167 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003168 }
3169
Stan Ilieveb868aa2017-02-21 11:06:16 -05003170 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003171 if (viewMatrix.rectStaysRect()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003172 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003173 }
3174
Stan Ilieveb868aa2017-02-21 11:06:16 -05003175 // Otherwise, if we have shader derivative support, render as device-independent
3176 if (shaderCaps->shaderDerivativeSupport()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003177 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
Stan Ilieveb868aa2017-02-21 11:06:16 -05003178 }
3179
bsalomon4b4a7cc2016-07-08 04:42:54 -07003180 return nullptr;
3181}
3182
3183///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003184
Robert Phillips7c525e62018-06-12 10:11:12 -04003185std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrContext* context,
3186 GrPaint&& paint,
3187 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003188 const SkRect& oval, SkScalar startAngle,
3189 SkScalar sweepAngle, bool useCenter,
3190 const GrStyle& style,
3191 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003192 SkASSERT(!oval.isEmpty());
3193 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003194 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003195 if (SkScalarAbs(sweepAngle) >= 360.f) {
3196 return nullptr;
3197 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003198 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3199 return nullptr;
3200 }
3201 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003202 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3203 useCenter};
Robert Phillips7c525e62018-06-12 10:11:12 -04003204 return CircleOp::Make(context, std::move(paint), viewMatrix,
3205 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003206}
3207
3208///////////////////////////////////////////////////////////////////////////////
3209
Hal Canary6f6961e2017-01-31 13:50:44 -05003210#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003211
Brian Salomon05441c42017-05-15 16:45:49 -04003212GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003213 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003214 SkScalar rotate = random->nextSScalar1() * 360.f;
3215 SkScalar translateX = random->nextSScalar1() * 1000.f;
3216 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003217 SkScalar scale;
3218 do {
3219 scale = random->nextSScalar1() * 100.f;
3220 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003221 SkMatrix viewMatrix;
3222 viewMatrix.setRotate(rotate);
3223 viewMatrix.postTranslate(translateX, translateY);
3224 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003225 SkRect circle = GrTest::TestSquare(random);
3226 SkPoint center = {circle.centerX(), circle.centerY()};
3227 SkScalar radius = circle.width() / 2.f;
3228 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003229 CircleOp::ArcParams arcParamsTmp;
3230 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003231 if (random->nextBool()) {
3232 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003233 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3234 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003235 arcParams = &arcParamsTmp;
3236 }
Robert Phillips7c525e62018-06-12 10:11:12 -04003237 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
3238 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003239 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003240 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003241 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003242 }
3243 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003244}
3245
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003246GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3247 SkScalar rotate = random->nextSScalar1() * 360.f;
3248 SkScalar translateX = random->nextSScalar1() * 1000.f;
3249 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003250 SkScalar scale;
3251 do {
3252 scale = random->nextSScalar1() * 100.f;
3253 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003254 SkMatrix viewMatrix;
3255 viewMatrix.setRotate(rotate);
3256 viewMatrix.postTranslate(translateX, translateY);
3257 viewMatrix.postScale(scale, scale);
3258 SkRect circle = GrTest::TestSquare(random);
3259 SkPoint center = {circle.centerX(), circle.centerY()};
3260 SkScalar radius = circle.width() / 2.f;
3261 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3262 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3263 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3264 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3265 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003266 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3267 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003268 startAngle, onAngle, offAngle, phase);
3269}
3270
Brian Salomon05441c42017-05-15 16:45:49 -04003271GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003272 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003273 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003274 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3275 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003276}
3277
Brian Salomon05441c42017-05-15 16:45:49 -04003278GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003279 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003280 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003281 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3282 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003283}
3284
Brian Salomon05441c42017-05-15 16:45:49 -04003285GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003286 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003287 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003288 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
3289 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003290}
3291
3292#endif