blob: cb7dade289d3136f314c59ea13d46fa13d952d94 [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 Verthd3420de2018-06-28 15:22:43 -0400598 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400599 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
600 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000601
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000602 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800603 if (egp.fStroke) {
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400604 fragBuilder->codeAppendf("offset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800605 ellipseRadii.fsIn());
Jim Van Verthb1b87d92018-06-28 10:46:05 -0400606 fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
607 fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800608 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
609 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000610 }
611
Brian Salomonea26d6b2018-01-23 20:33:21 +0000612 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000613 }
614
robertphillips46d36f02015-01-18 08:14:14 -0800615 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500616 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700617 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800618 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
619 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700620 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700621 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000622 }
623
bsalomona624bf32016-09-20 09:12:47 -0700624 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
625 FPCoordTransformIter&& transformIter) override {
626 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
627 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700628 }
629
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000630 private:
egdaniele659a582015-11-13 09:55:43 -0800631 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000632 };
633
Brian Salomon92be2f72018-06-19 14:33:47 -0400634 const Attribute& onVertexAttribute(int i) const override {
635 return IthAttribute(i, kInPosition, kInColor, kInEllipseOffset, kInEllipseRadii);
636 }
637
638 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
639 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
640 static constexpr Attribute kInEllipseOffset = {"inEllipseOffset", kHalf2_GrVertexAttribType};
641 static constexpr Attribute kInEllipseRadii = {"inEllipseRadii", kHalf4_GrVertexAttribType};
642
joshualitte3ababe2015-05-15 07:56:07 -0700643 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000644 bool fStroke;
645
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400646 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000647
joshualitt249af152014-09-15 11:41:13 -0700648 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000649};
Brian Salomon92be2f72018-06-19 14:33:47 -0400650constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInPosition;
651constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInColor;
652constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseOffset;
653constexpr GrPrimitiveProcessor::Attribute EllipseGeometryProcessor::kInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000654
bsalomoncdaa97b2016-03-08 08:30:14 -0800655GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000656
Hal Canary6f6961e2017-01-31 13:50:44 -0500657#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700658sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000659 return sk_sp<GrGeometryProcessor>(
660 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000661}
Hal Canary6f6961e2017-01-31 13:50:44 -0500662#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000663
664///////////////////////////////////////////////////////////////////////////////
665
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000666/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000667 * 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 +0000668 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
669 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
670 * using differentials.
671 *
672 * The result is device-independent and can be used with any affine matrix.
673 */
674
bsalomoncdaa97b2016-03-08 08:30:14 -0800675enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000676
bsalomoncdaa97b2016-03-08 08:30:14 -0800677class DIEllipseGeometryProcessor : public GrGeometryProcessor {
678public:
Brian Salomonea26d6b2018-01-23 20:33:21 +0000679 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400680 : INHERITED(kDIEllipseGeometryProcessor_ClassID)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000681 , fViewMatrix(viewMatrix) {
Brian Salomonea26d6b2018-01-23 20:33:21 +0000682 fStyle = style;
Brian Salomon92be2f72018-06-19 14:33:47 -0400683 this->setVertexAttributeCnt(4);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000684 }
685
Brian Salomond3b65972017-03-22 12:05:03 -0400686 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000687
mtklein36352bf2015-03-25 18:17:31 -0700688 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000689
Brian Salomon94efbf52016-11-29 13:43:05 -0500690 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700691 GLSLProcessor::GenKey(*this, caps, b);
692 }
halcanary9d524f22016-03-29 09:03:52 -0700693
Brian Salomon94efbf52016-11-29 13:43:05 -0500694 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700695 return new GLSLProcessor();
696 }
697
698private:
egdaniel57d3b032015-11-13 11:57:27 -0800699 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000700 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500701 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000702
joshualitt465283c2015-09-11 08:19:35 -0700703 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800704 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800705 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800706 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800707 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000708
joshualittabb52a12015-01-13 15:02:10 -0800709 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800710 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800711
Chris Dalton27372882017-12-08 13:34:21 -0700712 GrGLSLVarying offsets0(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800713 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon92be2f72018-06-19 14:33:47 -0400714 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.kInEllipseOffsets0.name());
joshualitt74077b92014-10-24 11:26:03 -0700715
Chris Dalton27372882017-12-08 13:34:21 -0700716 GrGLSLVarying offsets1(kHalf2_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800717 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon92be2f72018-06-19 14:33:47 -0400718 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.kInEllipseOffsets1.name());
joshualitt2dd1ae02014-12-03 06:24:10 -0800719
Chris Dalton60283612018-02-14 13:38:14 -0700720 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Brian Salomon92be2f72018-06-19 14:33:47 -0400721 varyingHandler->addPassThroughAttribute(diegp.kInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800722
joshualittabb52a12015-01-13 15:02:10 -0800723 // Setup position
Brian Salomon7f235432017-08-16 09:41:48 -0400724 this->writeOutputPosition(vertBuilder,
725 uniformHandler,
726 gpArgs,
Brian Salomon92be2f72018-06-19 14:33:47 -0400727 diegp.kInPosition.name(),
Brian Salomon7f235432017-08-16 09:41:48 -0400728 diegp.fViewMatrix,
729 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800730
731 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800732 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800733 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800734 uniformHandler,
Brian Salomon92be2f72018-06-19 14:33:47 -0400735 diegp.kInPosition.asShaderVar(),
bsalomona624bf32016-09-20 09:12:47 -0700736 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800737
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000738 // for outer curve
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400739 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
740 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
741 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
742 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500743 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400744 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
745 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500746 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000747
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400748 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000749 // avoid calling inversesqrt on zero.
Jim Van Verthd3420de2018-06-28 15:22:43 -0400750 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400751 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800752 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000753 // can probably do this with one step
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400754 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800755 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000756 } else {
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400757 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000758 }
759
760 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800761 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800762 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
763 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
764 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
765 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500766 fragBuilder->codeAppendf(
Ethan Nicholasf7b88202017-09-18 14:10:39 -0400767 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
768 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
Brian Salomon289e3d82016-12-14 15:52:56 -0500769 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800770 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
771 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000772 }
Brian Salomonea26d6b2018-01-23 20:33:21 +0000773
774 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000775 }
776
robertphillips46d36f02015-01-18 08:14:14 -0800777 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500778 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700779 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800780 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
781 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700782 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700783 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000784 }
785
bsalomona624bf32016-09-20 09:12:47 -0700786 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
787 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800788 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700789
bsalomon31df31c2016-08-17 09:00:24 -0700790 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
791 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700792 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800793 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700794 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
795 }
bsalomona624bf32016-09-20 09:12:47 -0700796 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000797 }
798
799 private:
joshualitt5559ca22015-05-21 15:50:36 -0700800 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700801 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800802
egdaniele659a582015-11-13 09:55:43 -0800803 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000804 };
805
Brian Salomon92be2f72018-06-19 14:33:47 -0400806 const Attribute& onVertexAttribute(int i) const override {
807 return IthAttribute(i, kInPosition, kInColor, kInEllipseOffsets0, kInEllipseOffsets1);
808 }
809
810 static constexpr Attribute kInPosition = {"inPosition", kFloat2_GrVertexAttribType};
811 static constexpr Attribute kInColor = {"inColor", kUByte4_norm_GrVertexAttribType};
812 static constexpr Attribute kInEllipseOffsets0 = {"inEllipseOffsets0",
813 kHalf2_GrVertexAttribType};
814 static constexpr Attribute kInEllipseOffsets1 = {"inEllipseOffsets1",
815 kHalf2_GrVertexAttribType};
816
Brian Salomon289e3d82016-12-14 15:52:56 -0500817 SkMatrix fViewMatrix;
818 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000819
Brian Salomon0c26a9d2017-07-06 10:09:38 -0400820 GR_DECLARE_GEOMETRY_PROCESSOR_TEST
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000821
joshualitt249af152014-09-15 11:41:13 -0700822 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000823};
Brian Salomon92be2f72018-06-19 14:33:47 -0400824constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInPosition;
825constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInColor;
826constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets0;
827constexpr GrPrimitiveProcessor::Attribute DIEllipseGeometryProcessor::kInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000828
bsalomoncdaa97b2016-03-08 08:30:14 -0800829GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000830
Hal Canary6f6961e2017-01-31 13:50:44 -0500831#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700832sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500833 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
Brian Salomonea26d6b2018-01-23 20:33:21 +0000834 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000835}
Hal Canary6f6961e2017-01-31 13:50:44 -0500836#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000837
838///////////////////////////////////////////////////////////////////////////////
839
jvanverth6ca48822016-10-07 06:57:32 -0700840// We have two possible cases for geometry for a circle:
841
842// In the case of a normal fill, we draw geometry for the circle as an octagon.
843static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500844 // enter the octagon
845 // clang-format off
846 0, 1, 8, 1, 2, 8,
847 2, 3, 8, 3, 4, 8,
848 4, 5, 8, 5, 6, 8,
849 6, 7, 8, 7, 0, 8
850 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700851};
852
853// For stroked circles, we use two nested octagons.
854static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500855 // enter the octagon
856 // clang-format off
857 0, 1, 9, 0, 9, 8,
858 1, 2, 10, 1, 10, 9,
859 2, 3, 11, 2, 11, 10,
860 3, 4, 12, 3, 12, 11,
861 4, 5, 13, 4, 13, 12,
862 5, 6, 14, 5, 14, 13,
863 6, 7, 15, 6, 15, 14,
864 7, 0, 8, 7, 8, 15,
865 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700866};
867
Brian Salomon289e3d82016-12-14 15:52:56 -0500868
jvanverth6ca48822016-10-07 06:57:32 -0700869static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
870static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
871static const int kVertsPerStrokeCircle = 16;
872static const int kVertsPerFillCircle = 9;
873
874static int circle_type_to_vert_count(bool stroked) {
875 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
876}
877
878static int circle_type_to_index_count(bool stroked) {
879 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
880}
881
882static const uint16_t* circle_type_to_indices(bool stroked) {
883 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
884}
885
886///////////////////////////////////////////////////////////////////////////////
887
Brian Salomon05441c42017-05-15 16:45:49 -0400888class CircleOp final : public GrMeshDrawOp {
889private:
890 using Helper = GrSimpleMeshDrawOpHelper;
891
joshualitt76e7fb62015-02-11 08:52:27 -0800892public:
Brian Salomon25a88092016-12-01 09:36:50 -0500893 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700894
bsalomon4f3a0ca2016-08-22 13:14:26 -0700895 /** Optional extra params to render a partial arc rather than a full circle. */
896 struct ArcParams {
897 SkScalar fStartAngleRadians;
898 SkScalar fSweepAngleRadians;
899 bool fUseCenter;
900 };
Brian Salomon05441c42017-05-15 16:45:49 -0400901
Robert Phillips7c525e62018-06-12 10:11:12 -0400902 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
903 GrPaint&& paint,
904 const SkMatrix& viewMatrix,
905 SkPoint center,
906 SkScalar radius,
907 const GrStyle& style,
Brian Salomon05441c42017-05-15 16:45:49 -0400908 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700909 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700910 if (style.hasPathEffect()) {
911 return nullptr;
912 }
Brian Salomon05441c42017-05-15 16:45:49 -0400913 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700914 SkStrokeRec::Style recStyle = stroke.getStyle();
915 if (arcParams) {
916 // Arc support depends on the style.
917 switch (recStyle) {
918 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500919 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700920 return nullptr;
921 case SkStrokeRec::kFill_Style:
922 // This supports all fills.
923 break;
Brian Salomon45c92202018-04-10 10:53:58 -0400924 case SkStrokeRec::kStroke_Style:
925 // Strokes that don't use the center point are supported with butt and round
926 // caps.
927 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
928 return nullptr;
929 }
930 break;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700931 case SkStrokeRec::kHairline_Style:
Brian Salomon45c92202018-04-10 10:53:58 -0400932 // Hairline only supports butt cap. Round caps could be emulated by slightly
933 // extending the angle range if we ever care to.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700934 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
935 return nullptr;
936 }
937 break;
938 }
939 }
Robert Phillips7c525e62018-06-12 10:11:12 -0400940 return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
941 radius, style, arcParams);
Brian Salomon05441c42017-05-15 16:45:49 -0400942 }
943
Brian Salomonea26d6b2018-01-23 20:33:21 +0000944 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
Brian Salomon05441c42017-05-15 16:45:49 -0400945 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
Brian Salomonea26d6b2018-01-23 20:33:21 +0000946 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -0400947 const SkStrokeRec& stroke = style.strokeRec();
948 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700949
Brian Salomon45c92202018-04-10 10:53:58 -0400950 fRoundCaps = false;
951
bsalomon4b4a7cc2016-07-08 04:42:54 -0700952 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700953 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700954 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800955
Brian Salomon289e3d82016-12-14 15:52:56 -0500956 bool isStrokeOnly =
957 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700958 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700959
jvanverth6ca48822016-10-07 06:57:32 -0700960 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700961 SkScalar outerRadius = radius;
962 SkScalar halfWidth = 0;
963 if (hasStroke) {
964 if (SkScalarNearlyZero(strokeWidth)) {
965 halfWidth = SK_ScalarHalf;
966 } else {
967 halfWidth = SkScalarHalf(strokeWidth);
968 }
969
970 outerRadius += halfWidth;
971 if (isStrokeOnly) {
972 innerRadius = radius - halfWidth;
973 }
974 }
975
976 // The radii are outset for two reasons. First, it allows the shader to simply perform
977 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
978 // Second, the outer radius is used to compute the verts of the bounding box that is
979 // rendered and the outset ensures the box will cover all partially covered by the circle.
980 outerRadius += SK_ScalarHalf;
981 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700982 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400983 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700984
bsalomon4f3a0ca2016-08-22 13:14:26 -0700985 // This makes every point fully inside the intersection plane.
986 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
987 // This makes every point fully outside the union plane.
988 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
Brian Salomon45c92202018-04-10 10:53:58 -0400989 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
bsalomon4f3a0ca2016-08-22 13:14:26 -0700990 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
991 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700992 if (arcParams) {
993 // The shader operates in a space where the circle is translated to be centered at the
994 // origin. Here we compute points on the unit circle at the starting and ending angles.
995 SkPoint startPoint, stopPoint;
996 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
997 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
998 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400999
1000 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
1001 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
1002 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
1003 startPoint.normalize();
1004 stopPoint.normalize();
1005
1006 // If the matrix included scale (on one axis) we need to swap our start and end points
1007 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001008 using std::swap;
1009 swap(startPoint, stopPoint);
Brian Osmanfd773fb2017-05-17 11:43:11 -04001010 }
1011
Brian Salomon45c92202018-04-10 10:53:58 -04001012 fRoundCaps = style.strokeRec().getWidth() > 0 &&
1013 style.strokeRec().getCap() == SkPaint::kRound_Cap;
1014 SkPoint roundCaps[2];
1015 if (fRoundCaps) {
1016 // Compute the cap center points in the normalized space.
1017 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
1018 roundCaps[0] = startPoint * midRadius;
1019 roundCaps[1] = stopPoint * midRadius;
1020 } else {
1021 roundCaps[0] = kUnusedRoundCaps[0];
1022 roundCaps[1] = kUnusedRoundCaps[1];
1023 }
1024
bsalomon4f3a0ca2016-08-22 13:14:26 -07001025 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
Brian Salomon45c92202018-04-10 10:53:58 -04001026 // radial lines. We treat round caps the same way, but tack coverage of circles at the
1027 // center of the butts.
1028 // However, in both cases we have to be careful about the half-circle.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001029 // case. In that case the two radial lines are equal and so that edge gets clipped
Brian Salomon45c92202018-04-10 10:53:58 -04001030 // twice. Since the shared edge goes through the center we fall back on the !useCenter
bsalomon4f3a0ca2016-08-22 13:14:26 -07001031 // case.
Brian Salomon45c92202018-04-10 10:53:58 -04001032 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
1033 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
1034 !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001035 if (useCenter) {
1036 SkVector norm0 = {startPoint.fY, -startPoint.fX};
1037 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
1038 if (arcParams->fSweepAngleRadians > 0) {
1039 norm0.negate();
1040 } else {
1041 norm1.negate();
1042 }
Brian Salomon05441c42017-05-15 16:45:49 -04001043 fClipPlane = true;
Brian Salomon45c92202018-04-10 10:53:58 -04001044 if (absSweep > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -04001045 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001046 color,
1047 innerRadius,
1048 outerRadius,
1049 {norm0.fX, norm0.fY, 0.5f},
1050 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1051 {norm1.fX, norm1.fY, 0.5f},
Brian Salomon45c92202018-04-10 10:53:58 -04001052 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001053 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001054 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001055 fClipPlaneIsect = false;
1056 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001057 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001058 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -07001059 color,
1060 innerRadius,
1061 outerRadius,
1062 {norm0.fX, norm0.fY, 0.5f},
1063 {norm1.fX, norm1.fY, 0.5f},
1064 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001065 {roundCaps[0], roundCaps[1]},
jvanverth6ca48822016-10-07 06:57:32 -07001066 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -05001067 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -04001068 fClipPlaneIsect = true;
1069 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001070 }
1071 } else {
1072 // We clip to a secant of the original circle.
1073 startPoint.scale(radius);
1074 stopPoint.scale(radius);
1075 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
1076 norm.normalize();
1077 if (arcParams->fSweepAngleRadians > 0) {
1078 norm.negate();
1079 }
1080 SkScalar d = -norm.dot(startPoint) + 0.5f;
1081
Brian Salomon05441c42017-05-15 16:45:49 -04001082 fCircles.emplace_back(
1083 Circle{color,
1084 innerRadius,
1085 outerRadius,
1086 {norm.fX, norm.fY, d},
1087 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1088 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001089 {roundCaps[0], roundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001090 devBounds,
1091 stroked});
1092 fClipPlane = true;
1093 fClipPlaneIsect = false;
1094 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001095 }
1096 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04001097 fCircles.emplace_back(
1098 Circle{color,
1099 innerRadius,
1100 outerRadius,
1101 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1102 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
1103 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
Brian Salomon45c92202018-04-10 10:53:58 -04001104 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
Brian Salomon05441c42017-05-15 16:45:49 -04001105 devBounds,
1106 stroked});
1107 fClipPlane = false;
1108 fClipPlaneIsect = false;
1109 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 }
bsalomon88cf17d2016-07-08 06:40:56 -07001111 // Use the original radius and stroke radius for the bounds so that it does not include the
1112 // AA bloat.
1113 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -04001114 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -05001115 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1116 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -04001117 fVertCount = circle_type_to_vert_count(stroked);
1118 fIndexCount = circle_type_to_index_count(stroked);
1119 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -08001120 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001121
Brian Salomon289e3d82016-12-14 15:52:56 -05001122 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001123
Robert Phillipsf1748f52017-09-14 14:11:24 -04001124 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001125 fHelper.visitProxies(func);
1126 }
1127
robertphillipse004bfc2015-11-16 09:06:59 -08001128 SkString dumpInfo() const override {
1129 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001130 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001131 string.appendf(
1132 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1133 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001134 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1135 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1136 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -08001137 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001138 string += fHelper.dumpInfo();
1139 string += INHERITED::dumpInfo();
robertphillipse004bfc2015-11-16 09:06:59 -08001140 return string;
1141 }
1142
Brian Osman9a725dd2017-09-20 09:53:22 -04001143 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1144 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001145 GrColor* color = &fCircles.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001146 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1147 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001148 }
1149
1150 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1151
bsalomone46f9fe2015-08-18 06:05:14 -07001152private:
Brian Salomon91326c32017-08-09 16:02:19 -04001153 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001154 SkMatrix localMatrix;
1155 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001156 return;
1157 }
1158
1159 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001160 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
Brian Salomon45c92202018-04-10 10:53:58 -04001161 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001162
1163 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001164 SkPoint fPos;
1165 GrColor fColor;
1166 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001167 SkScalar fOuterRadius;
1168 SkScalar fInnerRadius;
1169 // These planes may or may not be present in the vertex buffer.
1170 SkScalar fHalfPlanes[3][3];
1171 };
joshualitt76e7fb62015-02-11 08:52:27 -08001172
Brian Salomon45c92202018-04-10 10:53:58 -04001173 int numPlanes = (int)fClipPlane + fClipPlaneIsect + fClipPlaneUnion;
1174 auto vertexCapCenters = [numPlanes](CircleVertex* v) {
1175 return (void*)(v->fHalfPlanes + numPlanes);
1176 };
Brian Salomon92be2f72018-06-19 14:33:47 -04001177 size_t vertexStride = sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
1178 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
1179 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) +
1180 (fRoundCaps ? 2 * sizeof(SkPoint) : 0);
1181 SkASSERT(vertexStride == gp->debugOnly_vertexStride());
jvanverth6ca48822016-10-07 06:57:32 -07001182
1183 const GrBuffer* vertexBuffer;
1184 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -05001185 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
1186 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001187 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -07001188 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -08001189 return;
1190 }
1191
jvanverth6ca48822016-10-07 06:57:32 -07001192 const GrBuffer* indexBuffer = nullptr;
1193 int firstIndex = 0;
1194 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1195 if (!indices) {
1196 SkDebugf("Could not allocate indices\n");
1197 return;
1198 }
1199
1200 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001201 for (const auto& circle : fCircles) {
1202 SkScalar innerRadius = circle.fInnerRadius;
1203 SkScalar outerRadius = circle.fOuterRadius;
1204 GrColor color = circle.fColor;
1205 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001206
Brian Salomon289e3d82016-12-14 15:52:56 -05001207 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
1208 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
1209 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
1210 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
1211 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
1212 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
1213 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
1214 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001215
1216 // The inner radius in the vertex data must be specified in normalized space.
1217 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001218
1219 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -05001220 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -07001221 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -05001222
Brian Salomon289e3d82016-12-14 15:52:56 -05001223 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001224 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001225 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001226 v0->fOuterRadius = outerRadius;
1227 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001228
Brian Salomon289e3d82016-12-14 15:52:56 -05001229 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001230 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001231 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001232 v1->fOuterRadius = outerRadius;
1233 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001234
Brian Salomon289e3d82016-12-14 15:52:56 -05001235 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001236 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001237 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001238 v2->fOuterRadius = outerRadius;
1239 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001240
Brian Salomon289e3d82016-12-14 15:52:56 -05001241 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001242 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -07001243 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001244 v3->fOuterRadius = outerRadius;
1245 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001246
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001248 v4->fColor = color;
1249 v4->fOffset = SkPoint::Make(octOffset, 1);
1250 v4->fOuterRadius = outerRadius;
1251 v4->fInnerRadius = innerRadius;
1252
Brian Salomon289e3d82016-12-14 15:52:56 -05001253 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001254 v5->fColor = color;
1255 v5->fOffset = SkPoint::Make(-octOffset, 1);
1256 v5->fOuterRadius = outerRadius;
1257 v5->fInnerRadius = innerRadius;
1258
Brian Salomon289e3d82016-12-14 15:52:56 -05001259 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001260 v6->fColor = color;
1261 v6->fOffset = SkPoint::Make(-1, octOffset);
1262 v6->fOuterRadius = outerRadius;
1263 v6->fInnerRadius = innerRadius;
1264
Brian Salomon289e3d82016-12-14 15:52:56 -05001265 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -07001266 v7->fColor = color;
1267 v7->fOffset = SkPoint::Make(-1, -octOffset);
1268 v7->fOuterRadius = outerRadius;
1269 v7->fInnerRadius = innerRadius;
1270
bsalomon4f3a0ca2016-08-22 13:14:26 -07001271 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001272 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1273 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1274 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1275 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1276 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1277 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1278 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1279 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001280 }
1281 int unionIdx = 1;
1282 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001283 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1284 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1285 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1286 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1287 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1288 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1289 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1290 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001291 unionIdx = 2;
1292 }
1293 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001294 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1295 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1296 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1297 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1298 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1299 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1300 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1301 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001302 }
Brian Salomon45c92202018-04-10 10:53:58 -04001303 if (fRoundCaps) {
1304 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1305 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1306 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1307 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1308 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1309 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1310 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1311 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1312 }
jvanverth6ca48822016-10-07 06:57:32 -07001313
Brian Salomon05441c42017-05-15 16:45:49 -04001314 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -07001315 // compute the inner ring
1316 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1317 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
1318 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
1319 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
1320 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
1321 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
1322 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
1323 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
1324
1325 // cosine and sine of pi/8
1326 SkScalar c = 0.923579533f;
1327 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -04001328 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -07001329
Brian Salomon289e3d82016-12-14 15:52:56 -05001330 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001331 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001332 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001333 v0->fOuterRadius = outerRadius;
1334 v0->fInnerRadius = innerRadius;
1335
Brian Salomon289e3d82016-12-14 15:52:56 -05001336 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001337 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001338 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001339 v1->fOuterRadius = outerRadius;
1340 v1->fInnerRadius = innerRadius;
1341
Brian Salomon289e3d82016-12-14 15:52:56 -05001342 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001343 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001344 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001345 v2->fOuterRadius = outerRadius;
1346 v2->fInnerRadius = innerRadius;
1347
Brian Salomon289e3d82016-12-14 15:52:56 -05001348 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001349 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001350 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001351 v3->fOuterRadius = outerRadius;
1352 v3->fInnerRadius = innerRadius;
1353
Brian Salomon289e3d82016-12-14 15:52:56 -05001354 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001355 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001356 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001357 v4->fOuterRadius = outerRadius;
1358 v4->fInnerRadius = innerRadius;
1359
Brian Salomon289e3d82016-12-14 15:52:56 -05001360 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001361 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001362 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001363 v5->fOuterRadius = outerRadius;
1364 v5->fInnerRadius = innerRadius;
1365
Brian Salomon289e3d82016-12-14 15:52:56 -05001366 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001367 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001368 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001369 v6->fOuterRadius = outerRadius;
1370 v6->fInnerRadius = innerRadius;
1371
Brian Salomon289e3d82016-12-14 15:52:56 -05001372 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001373 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001374 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001375 v7->fOuterRadius = outerRadius;
1376 v7->fInnerRadius = innerRadius;
1377
1378 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001379 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1380 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1381 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1382 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1383 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1384 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1385 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1386 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001387 }
1388 int unionIdx = 1;
1389 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001390 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1391 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1392 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1393 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1394 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1395 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1396 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1397 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001398 unionIdx = 2;
1399 }
1400 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001401 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1402 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1403 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1404 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1405 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1406 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1407 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1408 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001409 }
Brian Salomon45c92202018-04-10 10:53:58 -04001410 if (fRoundCaps) {
1411 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1412 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1413 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1414 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1415 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1416 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1417 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1418 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint));
1419 }
jvanverth6ca48822016-10-07 06:57:32 -07001420 } else {
1421 // filled
1422 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1423 v8->fPos = center;
1424 v8->fColor = color;
1425 v8->fOffset = SkPoint::Make(0, 0);
1426 v8->fOuterRadius = outerRadius;
1427 v8->fInnerRadius = innerRadius;
1428 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001429 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001430 }
1431 int unionIdx = 1;
1432 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001433 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001434 unionIdx = 2;
1435 }
1436 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001437 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001438 }
Brian Salomon45c92202018-04-10 10:53:58 -04001439 SkASSERT(!fRoundCaps);
jvanverth6ca48822016-10-07 06:57:32 -07001440 }
1441
Brian Salomon05441c42017-05-15 16:45:49 -04001442 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1443 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001444 for (int i = 0; i < primIndexCount; ++i) {
1445 *indices++ = primIndices[i] + currStartVertex;
1446 }
1447
Brian Salomon05441c42017-05-15 16:45:49 -04001448 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1449 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001450 }
jvanverth6ca48822016-10-07 06:57:32 -07001451
Chris Dalton3809bab2017-06-13 10:55:06 -06001452 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04001453 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1454 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06001455 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001456 auto pipe = fHelper.makePipeline(target);
1457 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001458 }
1459
Brian Salomon25a88092016-12-01 09:36:50 -05001460 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001462
1463 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001464 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001465 return false;
1466 }
1467
Brian Salomon05441c42017-05-15 16:45:49 -04001468 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001469 return false;
1470 }
1471
Brian Salomon05441c42017-05-15 16:45:49 -04001472 if (fHelper.usesLocalCoords() &&
1473 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001474 return false;
1475 }
1476
Brian Salomon289e3d82016-12-14 15:52:56 -05001477 // Because we've set up the ops that don't use the planes with noop values
1478 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001479 fClipPlane |= that->fClipPlane;
1480 fClipPlaneIsect |= that->fClipPlaneIsect;
1481 fClipPlaneUnion |= that->fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001482 fRoundCaps |= that->fRoundCaps;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001483
Brian Salomon05441c42017-05-15 16:45:49 -04001484 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001485 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001486 fVertCount += that->fVertCount;
1487 fIndexCount += that->fIndexCount;
1488 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001489 return true;
1490 }
1491
Brian Salomon05441c42017-05-15 16:45:49 -04001492 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001493 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001494 SkScalar fInnerRadius;
1495 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001496 SkScalar fClipPlane[3];
1497 SkScalar fIsectPlane[3];
1498 SkScalar fUnionPlane[3];
Brian Salomon45c92202018-04-10 10:53:58 -04001499 SkPoint fRoundCapCenters[2];
Brian Salomon289e3d82016-12-14 15:52:56 -05001500 SkRect fDevBounds;
1501 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001502 };
1503
Brian Salomon289e3d82016-12-14 15:52:56 -05001504 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001505 Helper fHelper;
1506 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001507 int fVertCount;
1508 int fIndexCount;
1509 bool fAllFill;
1510 bool fClipPlane;
1511 bool fClipPlaneIsect;
1512 bool fClipPlaneUnion;
Brian Salomon45c92202018-04-10 10:53:58 -04001513 bool fRoundCaps;
reed1b55a962015-09-17 20:16:13 -07001514
Brian Salomon05441c42017-05-15 16:45:49 -04001515 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001516};
1517
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001518class ButtCapDashedCircleOp final : public GrMeshDrawOp {
1519private:
1520 using Helper = GrSimpleMeshDrawOpHelper;
1521
1522public:
1523 DEFINE_OP_CLASS_ID
1524
Robert Phillips7c525e62018-06-12 10:11:12 -04001525 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1526 GrPaint&& paint,
1527 const SkMatrix& viewMatrix,
1528 SkPoint center,
1529 SkScalar radius,
1530 SkScalar strokeWidth,
1531 SkScalar startAngle,
1532 SkScalar onAngle,
1533 SkScalar offAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001534 SkScalar phaseAngle) {
1535 SkASSERT(circle_stays_circle(viewMatrix));
1536 SkASSERT(strokeWidth < 2 * radius);
Robert Phillips7c525e62018-06-12 10:11:12 -04001537 return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
1538 center, radius, strokeWidth, startAngle,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001539 onAngle, offAngle, phaseAngle);
1540 }
1541
1542 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color,
1543 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
1544 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
1545 SkScalar offAngle, SkScalar phaseAngle)
1546 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1547 SkASSERT(circle_stays_circle(viewMatrix));
1548 viewMatrix.mapPoints(&center, 1);
1549 radius = viewMatrix.mapRadius(radius);
1550 strokeWidth = viewMatrix.mapRadius(strokeWidth);
1551
1552 // Determine the angle where the circle starts in device space and whether its orientation
1553 // has been reversed.
1554 SkVector start;
1555 bool reflection;
1556 if (!startAngle) {
1557 start = {1, 0};
1558 } else {
1559 start.fY = SkScalarSinCos(startAngle, &start.fX);
1560 }
1561 viewMatrix.mapVectors(&start, 1);
1562 startAngle = SkScalarATan2(start.fY, start.fX);
1563 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
1564 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
1565
1566 auto totalAngle = onAngle + offAngle;
1567 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
1568
1569 SkScalar halfWidth = 0;
1570 if (SkScalarNearlyZero(strokeWidth)) {
1571 halfWidth = SK_ScalarHalf;
1572 } else {
1573 halfWidth = SkScalarHalf(strokeWidth);
1574 }
1575
1576 SkScalar outerRadius = radius + halfWidth;
1577 SkScalar innerRadius = radius - halfWidth;
1578
1579 // The radii are outset for two reasons. First, it allows the shader to simply perform
1580 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1581 // Second, the outer radius is used to compute the verts of the bounding box that is
1582 // rendered and the outset ensures the box will cover all partially covered by the circle.
1583 outerRadius += SK_ScalarHalf;
1584 innerRadius -= SK_ScalarHalf;
1585 fViewMatrixIfUsingLocalCoords = viewMatrix;
1586
1587 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
1588 center.fX + outerRadius, center.fY + outerRadius);
1589
1590 // We store whether there is a reflection as a negative total angle.
1591 if (reflection) {
1592 totalAngle = -totalAngle;
1593 }
1594 fCircles.push_back(Circle{
1595 color,
1596 outerRadius,
1597 innerRadius,
1598 onAngle,
1599 totalAngle,
1600 startAngle,
1601 phaseAngle,
1602 devBounds
1603 });
1604 // Use the original radius and stroke radius for the bounds so that it does not include the
1605 // AA bloat.
1606 radius += halfWidth;
1607 this->setBounds(
1608 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
1609 HasAABloat::kYes, IsZeroArea::kNo);
1610 fVertCount = circle_type_to_vert_count(true);
1611 fIndexCount = circle_type_to_index_count(true);
1612 }
1613
1614 const char* name() const override { return "ButtCappedDashedCircleOp"; }
1615
1616 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); }
1617
1618 SkString dumpInfo() const override {
1619 SkString string;
1620 for (int i = 0; i < fCircles.count(); ++i) {
1621 string.appendf(
1622 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1623 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
1624 "Phase: %.2f\n",
1625 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
1626 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
1627 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle,
1628 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle);
1629 }
1630 string += fHelper.dumpInfo();
1631 string += INHERITED::dumpInfo();
1632 return string;
1633 }
1634
1635 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1636 GrPixelConfigIsClamped dstIsClamped) override {
1637 GrColor* color = &fCircles.front().fColor;
1638 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1639 GrProcessorAnalysisCoverage::kSingleChannel, color);
1640 }
1641
1642 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1643
1644private:
1645 void onPrepareDraws(Target* target) override {
1646 SkMatrix localMatrix;
1647 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1648 return;
1649 }
1650
1651 // Setup geometry processor
1652 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix));
1653
1654 struct CircleVertex {
1655 SkPoint fPos;
1656 GrColor fColor;
1657 SkPoint fOffset;
1658 SkScalar fOuterRadius;
1659 SkScalar fInnerRadius;
1660 SkScalar fOnAngle;
1661 SkScalar fTotalAngle;
1662 SkScalar fStartAngle;
1663 SkScalar fPhaseAngle;
1664 };
1665
Brian Salomon92be2f72018-06-19 14:33:47 -04001666 static constexpr size_t kVertexStride = sizeof(CircleVertex);
1667 SkASSERT(kVertexStride == gp->debugOnly_vertexStride());
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001668
1669 const GrBuffer* vertexBuffer;
1670 int firstVertex;
Brian Salomon92be2f72018-06-19 14:33:47 -04001671 char* vertices = (char*)target->makeVertexSpace(kVertexStride, fVertCount, &vertexBuffer,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001672 &firstVertex);
1673 if (!vertices) {
1674 SkDebugf("Could not allocate vertices\n");
1675 return;
1676 }
1677
1678 const GrBuffer* indexBuffer = nullptr;
1679 int firstIndex = 0;
1680 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1681 if (!indices) {
1682 SkDebugf("Could not allocate indices\n");
1683 return;
1684 }
1685
1686 int currStartVertex = 0;
1687 for (const auto& circle : fCircles) {
1688 // The inner radius in the vertex data must be specified in normalized space so that
1689 // length() can be called with smaller values to avoid precision issues with half
1690 // floats.
1691 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
1692 const SkRect& bounds = circle.fDevBounds;
1693 bool reflect = false;
1694 SkScalar totalAngle = circle.fTotalAngle;
1695 if (totalAngle < 0) {
1696 reflect = true;
1697 totalAngle = -totalAngle;
1698 }
1699
1700 // The bounding geometry for the circle is composed of an outer bounding octagon and
1701 // an inner bounded octagon.
1702
1703 // Initializes the attributes that are the same at each vertex. Also applies reflection.
1704 auto init_const_attrs_and_reflect = [&](CircleVertex* v) {
1705 v->fColor = circle.fColor;
1706 v->fOuterRadius = circle.fOuterRadius;
1707 v->fInnerRadius = normInnerRadius;
1708 v->fOnAngle = circle.fOnAngle;
1709 v->fTotalAngle = totalAngle;
1710 v->fStartAngle = circle.fStartAngle;
1711 v->fPhaseAngle = circle.fPhaseAngle;
1712 if (reflect) {
1713 v->fStartAngle = -v->fStartAngle;
1714 v->fOffset.fY = -v->fOffset.fY;
1715 }
1716 };
1717
1718 // Compute the vertices of the outer octagon.
1719 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
1720 SkScalar halfWidth = 0.5f * bounds.width();
1721 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) {
Brian Salomon92be2f72018-06-19 14:33:47 -04001722 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * kVertexStride);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001723 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth};
1724 v->fOffset = {x, y};
1725 init_const_attrs_and_reflect(v);
1726 };
1727 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
1728 init_outer_vertex(0, -kOctOffset, -1);
1729 init_outer_vertex(1, kOctOffset, -1);
1730 init_outer_vertex(2, 1, -kOctOffset);
1731 init_outer_vertex(3, 1, kOctOffset);
1732 init_outer_vertex(4, kOctOffset, 1);
1733 init_outer_vertex(5, -kOctOffset, 1);
1734 init_outer_vertex(6, -1, kOctOffset);
1735 init_outer_vertex(7, -1, -kOctOffset);
1736
1737 // Compute the vertices of the inner octagon.
1738 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) {
1739 CircleVertex* v =
Brian Salomon92be2f72018-06-19 14:33:47 -04001740 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * kVertexStride);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001741 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius};
1742 v->fOffset = {x * normInnerRadius, y * normInnerRadius};
1743 init_const_attrs_and_reflect(v);
1744 };
1745
1746 // cosine and sine of pi/8
1747 static constexpr SkScalar kCos = 0.923579533f;
1748 static constexpr SkScalar kSin = 0.382683432f;
1749
1750 init_inner_vertex(0, -kSin, -kCos);
1751 init_inner_vertex(1, kSin, -kCos);
1752 init_inner_vertex(2, kCos, -kSin);
1753 init_inner_vertex(3, kCos, kSin);
1754 init_inner_vertex(4, kSin, kCos);
1755 init_inner_vertex(5, -kSin, kCos);
1756 init_inner_vertex(6, -kCos, kSin);
1757 init_inner_vertex(7, -kCos, -kSin);
1758
1759 const uint16_t* primIndices = circle_type_to_indices(true);
1760 const int primIndexCount = circle_type_to_index_count(true);
1761 for (int i = 0; i < primIndexCount; ++i) {
1762 *indices++ = primIndices[i] + currStartVertex;
1763 }
1764
1765 currStartVertex += circle_type_to_vert_count(true);
Brian Salomon92be2f72018-06-19 14:33:47 -04001766 vertices += circle_type_to_vert_count(true) * kVertexStride;
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001767 }
1768
1769 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04001770 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
1771 GrPrimitiveRestart::kNo);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001772 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04001773 auto pipe = fHelper.makePipeline(target);
1774 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04001775 }
1776
1777 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1778 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
1779
1780 // can only represent 65535 unique vertices with 16-bit indices
1781 if (fVertCount + that->fVertCount > 65536) {
1782 return false;
1783 }
1784
1785 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1786 return false;
1787 }
1788
1789 if (fHelper.usesLocalCoords() &&
1790 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1791 return false;
1792 }
1793
1794 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1795 this->joinBounds(*that);
1796 fVertCount += that->fVertCount;
1797 fIndexCount += that->fIndexCount;
1798 return true;
1799 }
1800
1801 struct Circle {
1802 GrColor fColor;
1803 SkScalar fOuterRadius;
1804 SkScalar fInnerRadius;
1805 SkScalar fOnAngle;
1806 SkScalar fTotalAngle;
1807 SkScalar fStartAngle;
1808 SkScalar fPhaseAngle;
1809 SkRect fDevBounds;
1810 };
1811
1812 SkMatrix fViewMatrixIfUsingLocalCoords;
1813 Helper fHelper;
1814 SkSTArray<1, Circle, true> fCircles;
1815 int fVertCount;
1816 int fIndexCount;
1817
1818 typedef GrMeshDrawOp INHERITED;
1819};
1820
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001821///////////////////////////////////////////////////////////////////////////////
1822
Brian Salomon05441c42017-05-15 16:45:49 -04001823class EllipseOp : public GrMeshDrawOp {
1824private:
1825 using Helper = GrSimpleMeshDrawOpHelper;
1826
1827 struct DeviceSpaceParams {
1828 SkPoint fCenter;
1829 SkScalar fXRadius;
1830 SkScalar fYRadius;
1831 SkScalar fInnerXRadius;
1832 SkScalar fInnerYRadius;
1833 };
1834
joshualitt76e7fb62015-02-11 08:52:27 -08001835public:
Brian Salomon25a88092016-12-01 09:36:50 -05001836 DEFINE_OP_CLASS_ID
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001837
Robert Phillips7c525e62018-06-12 10:11:12 -04001838 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
1839 GrPaint&& paint,
1840 const SkMatrix& viewMatrix,
1841 const SkRect& ellipse,
1842 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04001843 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001844 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001845 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1846 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001847 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1848 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001849 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1850 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1851 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1852 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001853
bsalomon4b4a7cc2016-07-08 04:42:54 -07001854 // do (potentially) anisotropic mapping of stroke
1855 SkVector scaledStroke;
1856 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001857 scaledStroke.fX = SkScalarAbs(
1858 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1859 scaledStroke.fY = SkScalarAbs(
1860 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001861
1862 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001863 bool isStrokeOnly =
1864 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001865 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1866
Brian Salomon05441c42017-05-15 16:45:49 -04001867 params.fInnerXRadius = 0;
1868 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001869 if (hasStroke) {
1870 if (SkScalarNearlyZero(scaledStroke.length())) {
1871 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1872 } else {
1873 scaledStroke.scale(SK_ScalarHalf);
1874 }
1875
1876 // we only handle thick strokes for near-circular ellipses
1877 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001878 (0.5f * params.fXRadius > params.fYRadius ||
1879 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001880 return nullptr;
1881 }
1882
1883 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001884 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1885 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1886 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1887 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001888 return nullptr;
1889 }
1890
1891 // this is legit only if scale & translation (which should be the case at the moment)
1892 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001893 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1894 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001895 }
1896
Brian Salomon05441c42017-05-15 16:45:49 -04001897 params.fXRadius += scaledStroke.fX;
1898 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001899 }
Robert Phillips7c525e62018-06-12 10:11:12 -04001900 return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
1901 params, stroke);
Brian Salomon05441c42017-05-15 16:45:49 -04001902 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001903
Brian Salomonea26d6b2018-01-23 20:33:21 +00001904 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1905 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1906 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04001907 SkStrokeRec::Style style = stroke.getStyle();
1908 bool isStrokeOnly =
1909 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001910
Brian Salomon05441c42017-05-15 16:45:49 -04001911 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1912 params.fInnerXRadius, params.fInnerYRadius,
1913 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1914 params.fCenter.fY - params.fYRadius,
1915 params.fCenter.fX + params.fXRadius,
1916 params.fCenter.fY + params.fYRadius)});
1917
1918 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001919
bsalomon4b4a7cc2016-07-08 04:42:54 -07001920 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001921 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001922
Brian Salomon05441c42017-05-15 16:45:49 -04001923 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1924 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001925 }
joshualitt76e7fb62015-02-11 08:52:27 -08001926
Brian Salomon289e3d82016-12-14 15:52:56 -05001927 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001928
Robert Phillipsf1748f52017-09-14 14:11:24 -04001929 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04001930 fHelper.visitProxies(func);
1931 }
1932
Brian Salomon7c3e7182016-12-01 09:35:30 -05001933 SkString dumpInfo() const override {
1934 SkString string;
1935 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001936 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001937 string.appendf(
1938 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1939 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1940 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1941 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1942 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001943 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04001944 string += fHelper.dumpInfo();
1945 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05001946 return string;
1947 }
1948
Brian Osman9a725dd2017-09-20 09:53:22 -04001949 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
1950 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04001951 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04001952 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
1953 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04001954 }
1955
1956 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1957
bsalomone46f9fe2015-08-18 06:05:14 -07001958private:
Brian Salomon91326c32017-08-09 16:02:19 -04001959 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001960 SkMatrix localMatrix;
1961 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001962 return;
1963 }
1964
1965 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00001966 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001967
bsalomonb5238a72015-05-05 07:49:49 -07001968 QuadHelper helper;
Brian Salomon92be2f72018-06-19 14:33:47 -04001969 SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
Brian Salomon05441c42017-05-15 16:45:49 -04001970 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04001971 helper.init(target, sizeof(EllipseVertex), fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001972 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001973 return;
1974 }
1975
Brian Salomon05441c42017-05-15 16:45:49 -04001976 for (const auto& ellipse : fEllipses) {
1977 GrColor color = ellipse.fColor;
1978 SkScalar xRadius = ellipse.fXRadius;
1979 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001980
1981 // Compute the reciprocals of the radii here to save time in the shader
1982 SkScalar xRadRecip = SkScalarInvert(xRadius);
1983 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001984 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1985 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
vjiaoblack977996d2016-06-30 12:20:54 -07001986 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1987 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1988
Jim Van Verthb1b87d92018-06-28 10:46:05 -04001989 if (!fStroked) {
1990 // For filled ellipses we map a unit circle in the vertex attributes rather than
1991 // computing an ellipse and modifying that distance, so we normalize to 1
1992 xMaxOffset /= xRadius;
1993 yMaxOffset /= yRadius;
1994 }
1995
joshualitt76e7fb62015-02-11 08:52:27 -08001996 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001997 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001998 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001999 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002000 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2001 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2002
Brian Salomon05441c42017-05-15 16:45:49 -04002003 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002004 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07002005 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002006 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2007 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2008
Brian Salomon57caa662017-10-18 12:21:05 +00002009 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002010 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002011 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002012 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2013 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2014
Brian Salomon57caa662017-10-18 12:21:05 +00002015 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002016 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002017 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08002018 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2019 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2020
bsalomonb5238a72015-05-05 07:49:49 -07002021 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002022 }
Brian Salomon49348902018-06-26 09:12:38 -04002023 auto pipe = fHelper.makePipeline(target);
2024 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002025 }
2026
Brian Salomon25a88092016-12-01 09:36:50 -05002027 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002028 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002029
Brian Salomon05441c42017-05-15 16:45:49 -04002030 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002031 return false;
2032 }
2033
bsalomoncdaa97b2016-03-08 08:30:14 -08002034 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002035 return false;
2036 }
2037
Brian Salomon05441c42017-05-15 16:45:49 -04002038 if (fHelper.usesLocalCoords() &&
2039 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002040 return false;
2041 }
2042
Brian Salomon05441c42017-05-15 16:45:49 -04002043 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002044 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002045 return true;
2046 }
2047
Brian Salomon05441c42017-05-15 16:45:49 -04002048 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002049 GrColor fColor;
2050 SkScalar fXRadius;
2051 SkScalar fYRadius;
2052 SkScalar fInnerXRadius;
2053 SkScalar fInnerYRadius;
2054 SkRect fDevBounds;
2055 };
joshualitt76e7fb62015-02-11 08:52:27 -08002056
Brian Salomon289e3d82016-12-14 15:52:56 -05002057 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002058 Helper fHelper;
2059 bool fStroked;
2060 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002061
Brian Salomon05441c42017-05-15 16:45:49 -04002062 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002063};
2064
joshualitt76e7fb62015-02-11 08:52:27 -08002065/////////////////////////////////////////////////////////////////////////////////////////////////
2066
Brian Salomon05441c42017-05-15 16:45:49 -04002067class DIEllipseOp : public GrMeshDrawOp {
2068private:
2069 using Helper = GrSimpleMeshDrawOpHelper;
2070
2071 struct DeviceSpaceParams {
2072 SkPoint fCenter;
2073 SkScalar fXRadius;
2074 SkScalar fYRadius;
2075 SkScalar fInnerXRadius;
2076 SkScalar fInnerYRadius;
2077 DIEllipseStyle fStyle;
2078 };
2079
joshualitt76e7fb62015-02-11 08:52:27 -08002080public:
Brian Salomon25a88092016-12-01 09:36:50 -05002081 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002082
Robert Phillips7c525e62018-06-12 10:11:12 -04002083 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2084 GrPaint&& paint,
2085 const SkMatrix& viewMatrix,
2086 const SkRect& ellipse,
2087 const SkStrokeRec& stroke) {
Brian Salomon05441c42017-05-15 16:45:49 -04002088 DeviceSpaceParams params;
2089 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
2090 params.fXRadius = SkScalarHalf(ellipse.width());
2091 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08002092
bsalomon4b4a7cc2016-07-08 04:42:54 -07002093 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04002094 params.fStyle = (SkStrokeRec::kStroke_Style == style)
2095 ? DIEllipseStyle::kStroke
2096 : (SkStrokeRec::kHairline_Style == style)
2097 ? DIEllipseStyle::kHairline
2098 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002099
Brian Salomon05441c42017-05-15 16:45:49 -04002100 params.fInnerXRadius = 0;
2101 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002102 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
2103 SkScalar strokeWidth = stroke.getWidth();
2104
2105 if (SkScalarNearlyZero(strokeWidth)) {
2106 strokeWidth = SK_ScalarHalf;
2107 } else {
2108 strokeWidth *= SK_ScalarHalf;
2109 }
2110
2111 // we only handle thick strokes for near-circular ellipses
2112 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04002113 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
2114 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002115 return nullptr;
2116 }
2117
2118 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04002119 if (strokeWidth * (params.fYRadius * params.fYRadius) <
2120 (strokeWidth * strokeWidth) * params.fXRadius) {
2121 return nullptr;
2122 }
2123 if (strokeWidth * (params.fXRadius * params.fXRadius) <
2124 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002125 return nullptr;
2126 }
2127
2128 // set inner radius (if needed)
2129 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04002130 params.fInnerXRadius = params.fXRadius - strokeWidth;
2131 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002132 }
2133
Brian Salomon05441c42017-05-15 16:45:49 -04002134 params.fXRadius += strokeWidth;
2135 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002136 }
Brian Salomon05441c42017-05-15 16:45:49 -04002137 if (DIEllipseStyle::kStroke == params.fStyle &&
2138 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
2139 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002140 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002141 return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
Brian Salomon05441c42017-05-15 16:45:49 -04002142 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002143
Brian Salomonea26d6b2018-01-23 20:33:21 +00002144 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
2145 const SkMatrix& viewMatrix)
2146 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002147 // This expands the outer rect so that after CTM we end up with a half-pixel border
2148 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
2149 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
2150 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
2151 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05002152 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
2153 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002154
Brian Salomon05441c42017-05-15 16:45:49 -04002155 fEllipses.emplace_back(
2156 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
2157 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
2158 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
2159 params.fCenter.fY - params.fYRadius - geoDy,
2160 params.fCenter.fX + params.fXRadius + geoDx,
2161 params.fCenter.fY + params.fYRadius + geoDy)});
2162 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
2163 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08002164 }
2165
Brian Salomon289e3d82016-12-14 15:52:56 -05002166 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002167
Robert Phillipsf1748f52017-09-14 14:11:24 -04002168 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002169 fHelper.visitProxies(func);
2170 }
2171
Brian Salomon7c3e7182016-12-01 09:35:30 -05002172 SkString dumpInfo() const override {
2173 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002174 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002175 string.appendf(
2176 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
2177 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
2178 "GeoDY: %.2f\n",
2179 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
2180 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2181 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002182 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002183 string += fHelper.dumpInfo();
2184 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002185 return string;
2186 }
2187
Brian Osman9a725dd2017-09-20 09:53:22 -04002188 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2189 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002190 GrColor* color = &fEllipses.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002191 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2192 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002193 }
2194
2195 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2196
bsalomone46f9fe2015-08-18 06:05:14 -07002197private:
Brian Salomon91326c32017-08-09 16:02:19 -04002198 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002199 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002200 sk_sp<GrGeometryProcessor> gp(
Brian Salomonea26d6b2018-01-23 20:33:21 +00002201 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08002202
Brian Salomon92be2f72018-06-19 14:33:47 -04002203 SkASSERT(sizeof(DIEllipseVertex) == gp->debugOnly_vertexStride());
bsalomonb5238a72015-05-05 07:49:49 -07002204 QuadHelper helper;
2205 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04002206 helper.init(target, sizeof(DIEllipseVertex), fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002207 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002208 return;
2209 }
2210
Brian Salomon05441c42017-05-15 16:45:49 -04002211 for (const auto& ellipse : fEllipses) {
2212 GrColor color = ellipse.fColor;
2213 SkScalar xRadius = ellipse.fXRadius;
2214 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002215
Brian Salomon05441c42017-05-15 16:45:49 -04002216 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002217
2218 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04002219 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
2220 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002221
joshualitt76e7fb62015-02-11 08:52:27 -08002222 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002223 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002224 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002225 verts[0].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002226
Brian Salomon289e3d82016-12-14 15:52:56 -05002227 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002228 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002229 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002230 verts[1].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002231
Brian Salomon57caa662017-10-18 12:21:05 +00002232 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08002233 verts[2].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002234 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002235 verts[2].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
joshualitt76e7fb62015-02-11 08:52:27 -08002236
Brian Salomon57caa662017-10-18 12:21:05 +00002237 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08002238 verts[3].fColor = color;
Brian Salomon57caa662017-10-18 12:21:05 +00002239 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
Greg Daniel75a13022018-04-04 08:59:20 -04002240 verts[3].fInnerOffset = SkPoint::Make(0.0f, 0.0f);
2241
2242 if (DIEllipseStyle::kStroke == this->style()) {
2243 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
2244 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
2245
2246 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2247 -innerRatioY - offsetDy);
2248 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx,
2249 innerRatioY + offsetDy);
2250 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2251 -innerRatioY - offsetDy);
2252 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx,
2253 innerRatioY + offsetDy);
2254 }
joshualitt76e7fb62015-02-11 08:52:27 -08002255
bsalomonb5238a72015-05-05 07:49:49 -07002256 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08002257 }
Brian Salomon49348902018-06-26 09:12:38 -04002258 auto pipe = fHelper.makePipeline(target);
2259 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002260 }
halcanary9d524f22016-03-29 09:03:52 -07002261
Brian Salomon25a88092016-12-01 09:36:50 -05002262 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002263 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04002264 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002265 return false;
2266 }
2267
bsalomoncdaa97b2016-03-08 08:30:14 -08002268 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08002269 return false;
2270 }
2271
joshualittd96a67b2015-05-05 14:09:05 -07002272 // TODO rewrite to allow positioning on CPU
2273 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08002274 return false;
2275 }
2276
Brian Salomon05441c42017-05-15 16:45:49 -04002277 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002278 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002279 return true;
2280 }
2281
Brian Salomon05441c42017-05-15 16:45:49 -04002282 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
2283 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08002284
Brian Salomon05441c42017-05-15 16:45:49 -04002285 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002286 SkMatrix fViewMatrix;
2287 GrColor fColor;
2288 SkScalar fXRadius;
2289 SkScalar fYRadius;
2290 SkScalar fInnerXRadius;
2291 SkScalar fInnerYRadius;
2292 SkScalar fGeoDx;
2293 SkScalar fGeoDy;
2294 DIEllipseStyle fStyle;
2295 SkRect fBounds;
2296 };
2297
Brian Salomon05441c42017-05-15 16:45:49 -04002298 Helper fHelper;
2299 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07002300
Brian Salomon05441c42017-05-15 16:45:49 -04002301 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002302};
2303
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002304///////////////////////////////////////////////////////////////////////////////
2305
Mike Kleinfc6c37b2016-09-27 09:34:10 -04002306// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07002307//
2308// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
2309// ____________
2310// |_|________|_|
2311// | | | |
2312// | | | |
2313// | | | |
2314// |_|________|_|
2315// |_|________|_|
2316//
2317// For strokes, we don't draw the center quad.
2318//
2319// For circular roundrects, in the case where the stroke width is greater than twice
2320// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07002321// in the center. The shared vertices are duplicated so we can set a different outer radius
2322// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07002323// ____________
2324// |_|________|_|
2325// | |\ ____ /| |
2326// | | | | | |
2327// | | |____| | |
2328// |_|/______\|_|
2329// |_|________|_|
2330//
2331// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04002332//
2333// For filled rrects that need to provide a distance vector we resuse the overstroke
2334// geometry but make the inner rect degenerate (either a point or a horizontal or
2335// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07002336
jvanverth84839f62016-08-29 10:16:40 -07002337static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05002338 // clang-format off
2339 // overstroke quads
2340 // we place this at the beginning so that we can skip these indices when rendering normally
2341 16, 17, 19, 16, 19, 18,
2342 19, 17, 23, 19, 23, 21,
2343 21, 23, 22, 21, 22, 20,
2344 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07002345
Brian Salomon289e3d82016-12-14 15:52:56 -05002346 // corners
2347 0, 1, 5, 0, 5, 4,
2348 2, 3, 7, 2, 7, 6,
2349 8, 9, 13, 8, 13, 12,
2350 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002351
Brian Salomon289e3d82016-12-14 15:52:56 -05002352 // edges
2353 1, 2, 6, 1, 6, 5,
2354 4, 5, 9, 4, 9, 8,
2355 6, 7, 11, 6, 11, 10,
2356 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002357
Brian Salomon289e3d82016-12-14 15:52:56 -05002358 // center
2359 // we place this at the end so that we can ignore these indices when not rendering as filled
2360 5, 6, 10, 5, 10, 9,
2361 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002362};
Brian Salomon289e3d82016-12-14 15:52:56 -05002363
jvanverth84839f62016-08-29 10:16:40 -07002364// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05002365static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002366
jvanverth84839f62016-08-29 10:16:40 -07002367// overstroke count is arraysize minus the center indices
2368static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
2369// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05002370static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07002371// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07002372static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
2373static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07002374static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002375
jvanverthc3d0e422016-08-25 08:12:35 -07002376enum RRectType {
2377 kFill_RRectType,
2378 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07002379 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07002380};
2381
jvanverth84839f62016-08-29 10:16:40 -07002382static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002383 switch (type) {
2384 case kFill_RRectType:
2385 case kStroke_RRectType:
2386 return kVertsPerStandardRRect;
2387 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002388 return kVertsPerOverstrokeRRect;
2389 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002390 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002391 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002392}
2393
2394static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002395 switch (type) {
2396 case kFill_RRectType:
2397 return kIndicesPerFillRRect;
2398 case kStroke_RRectType:
2399 return kIndicesPerStrokeRRect;
2400 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002401 return kIndicesPerOverstrokeRRect;
2402 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002403 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002404 return 0;
jvanverth84839f62016-08-29 10:16:40 -07002405}
2406
2407static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002408 switch (type) {
2409 case kFill_RRectType:
2410 case kStroke_RRectType:
2411 return gStandardRRectIndices;
2412 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05002413 return gOverstrokeRRectIndices;
2414 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -04002415 SK_ABORT("Invalid type");
Brian Salomon289e3d82016-12-14 15:52:56 -05002416 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07002417}
2418
joshualitt76e7fb62015-02-11 08:52:27 -08002419///////////////////////////////////////////////////////////////////////////////////////////////////
2420
Robert Phillips79839d42016-10-06 15:03:34 -04002421// For distance computations in the interior of filled rrects we:
2422//
2423// add a interior degenerate (point or line) rect
2424// each vertex of that rect gets -outerRad as its radius
2425// this makes the computation of the distance to the outer edge be negative
2426// negative values are caught and then handled differently in the GP's onEmitCode
2427// each vertex is also given the normalized x & y distance from the interior rect's edge
2428// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
2429
Brian Salomon05441c42017-05-15 16:45:49 -04002430class CircularRRectOp : public GrMeshDrawOp {
2431private:
2432 using Helper = GrSimpleMeshDrawOpHelper;
2433
joshualitt76e7fb62015-02-11 08:52:27 -08002434public:
Brian Salomon25a88092016-12-01 09:36:50 -05002435 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002436
bsalomon4b4a7cc2016-07-08 04:42:54 -07002437 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
2438 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002439 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2440 GrPaint&& paint,
2441 const SkMatrix& viewMatrix,
2442 const SkRect& devRect,
2443 float devRadius,
2444 float devStrokeWidth,
2445 bool strokeOnly) {
2446 return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
2447 devRect, devRadius,
2448 devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04002449 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00002450 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
2451 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04002452 : INHERITED(ClassID())
2453 , fViewMatrixIfUsingLocalCoords(viewMatrix)
Brian Salomonea26d6b2018-01-23 20:33:21 +00002454 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002455 SkRect bounds = devRect;
2456 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
2457 SkScalar innerRadius = 0.0f;
2458 SkScalar outerRadius = devRadius;
2459 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07002460 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002461 if (devStrokeWidth > 0) {
2462 if (SkScalarNearlyZero(devStrokeWidth)) {
2463 halfWidth = SK_ScalarHalf;
2464 } else {
2465 halfWidth = SkScalarHalf(devStrokeWidth);
2466 }
joshualitt76e7fb62015-02-11 08:52:27 -08002467
bsalomon4b4a7cc2016-07-08 04:42:54 -07002468 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07002469 // Outset stroke by 1/4 pixel
2470 devStrokeWidth += 0.25f;
2471 // If stroke is greater than width or height, this is still a fill
2472 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05002473 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07002474 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07002475 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07002476 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002477 }
2478 outerRadius += halfWidth;
2479 bounds.outset(halfWidth, halfWidth);
2480 }
bsalomoncdaa97b2016-03-08 08:30:14 -08002481
bsalomon4b4a7cc2016-07-08 04:42:54 -07002482 // The radii are outset for two reasons. First, it allows the shader to simply perform
2483 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2484 // Second, the outer radius is used to compute the verts of the bounding box that is
2485 // rendered and the outset ensures the box will cover all partially covered by the rrect
2486 // corners.
2487 outerRadius += SK_ScalarHalf;
2488 innerRadius -= SK_ScalarHalf;
2489
bsalomon88cf17d2016-07-08 06:40:56 -07002490 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2491
2492 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07002493 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2494
Brian Salomon05441c42017-05-15 16:45:49 -04002495 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07002496 fVertCount = rrect_type_to_vert_count(type);
2497 fIndexCount = rrect_type_to_index_count(type);
2498 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08002499 }
2500
Brian Salomon289e3d82016-12-14 15:52:56 -05002501 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002502
Robert Phillipsf1748f52017-09-14 14:11:24 -04002503 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002504 fHelper.visitProxies(func);
2505 }
2506
jvanverthc3d0e422016-08-25 08:12:35 -07002507 SkString dumpInfo() const override {
2508 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04002509 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002510 string.appendf(
2511 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
2512 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04002513 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
2514 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
2515 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07002516 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002517 string += fHelper.dumpInfo();
2518 string += INHERITED::dumpInfo();
jvanverthc3d0e422016-08-25 08:12:35 -07002519 return string;
2520 }
2521
Brian Osman9a725dd2017-09-20 09:53:22 -04002522 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2523 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002524 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002525 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2526 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002527 }
2528
2529 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2530
Brian Salomon92aee3d2016-12-21 09:20:25 -05002531private:
Robert Phillips79839d42016-10-06 15:03:34 -04002532 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05002533 SkPoint fPos;
2534 GrColor fColor;
2535 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04002536 SkScalar fOuterRadius;
2537 SkScalar fInnerRadius;
2538 // No half plane, we don't use it here.
2539 };
2540
Brian Salomon289e3d82016-12-14 15:52:56 -05002541 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
2542 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
2543 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04002544 SkASSERT(smInset < bigInset);
2545
2546 // TL
2547 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
2548 (*verts)->fColor = color;
2549 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
2550 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002551 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002552 (*verts)++;
2553
2554 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05002555 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002556 (*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
Brian Salomon289e3d82016-12-14 15:52:56 -05002562 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002563 (*verts)->fColor = color;
2564 (*verts)->fOffset = SkPoint::Make(0, 0);
2565 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002566 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002567 (*verts)++;
2568
Brian Salomon289e3d82016-12-14 15:52:56 -05002569 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04002570 (*verts)->fColor = color;
2571 (*verts)->fOffset = SkPoint::Make(0, 0);
2572 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002573 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002574 (*verts)++;
2575
2576 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
2577 (*verts)->fColor = color;
2578 (*verts)->fOffset = SkPoint::Make(0, 0);
2579 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002580 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002581 (*verts)++;
2582
2583 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
2584 (*verts)->fColor = color;
2585 (*verts)->fOffset = SkPoint::Make(0, 0);
2586 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04002587 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04002588 (*verts)++;
2589
2590 // BL
2591 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
2592 (*verts)->fColor = color;
2593 (*verts)->fOffset = SkPoint::Make(xOffset, 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 // BR
2599 (*verts)->fPos = SkPoint::Make(bounds.fRight - 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
Brian Salomon91326c32017-08-09 16:02:19 -04002607 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002608 // Invert the view matrix as a local matrix (if any other processors require coords).
2609 SkMatrix localMatrix;
2610 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002611 return;
2612 }
2613
2614 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05002615 sk_sp<GrGeometryProcessor> gp(
Brian Salomon45c92202018-04-10 10:53:58 -04002616 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002617
Brian Salomon92be2f72018-06-19 14:33:47 -04002618 SkASSERT(sizeof(CircleVertex) == gp->debugOnly_vertexStride());
joshualitt76e7fb62015-02-11 08:52:27 -08002619
jvanverth84839f62016-08-29 10:16:40 -07002620 const GrBuffer* vertexBuffer;
2621 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08002622
Brian Salomon92be2f72018-06-19 14:33:47 -04002623 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
2624 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07002625 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08002626 SkDebugf("Could not allocate vertices\n");
2627 return;
2628 }
2629
jvanverth84839f62016-08-29 10:16:40 -07002630 const GrBuffer* indexBuffer = nullptr;
2631 int firstIndex = 0;
2632 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
2633 if (!indices) {
2634 SkDebugf("Could not allocate indices\n");
2635 return;
2636 }
2637
2638 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04002639 for (const auto& rrect : fRRects) {
2640 GrColor color = rrect.fColor;
2641 SkScalar outerRadius = rrect.fOuterRadius;
2642 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002643
Brian Salomon289e3d82016-12-14 15:52:56 -05002644 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
2645 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08002646
Brian Salomon289e3d82016-12-14 15:52:56 -05002647 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08002648 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07002649 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04002650 SkScalar innerRadius = rrect.fType != kFill_RRectType
2651 ? rrect.fInnerRadius / rrect.fOuterRadius
2652 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08002653 for (int i = 0; i < 4; ++i) {
2654 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002655 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002656 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
2657 verts->fOuterRadius = outerRadius;
2658 verts->fInnerRadius = innerRadius;
2659 verts++;
2660
2661 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002662 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002663 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2664 verts->fOuterRadius = outerRadius;
2665 verts->fInnerRadius = innerRadius;
2666 verts++;
2667
2668 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002669 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002670 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
2671 verts->fOuterRadius = outerRadius;
2672 verts->fInnerRadius = innerRadius;
2673 verts++;
2674
2675 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002676 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002677 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
2678 verts->fOuterRadius = outerRadius;
2679 verts->fInnerRadius = innerRadius;
2680 verts++;
2681 }
jvanverthc3d0e422016-08-25 08:12:35 -07002682 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07002683 // Effectively this is an additional stroked rrect, with its
2684 // outer radius = outerRadius - innerRadius, and inner radius = 0.
2685 // This will give us correct AA in the center and the correct
2686 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07002687 //
jvanvertha4f1af82016-08-29 07:17:47 -07002688 // Also, the outer offset is a constant vector pointing to the right, which
2689 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002690 if (kOverstroke_RRectType == rrect.fType) {
2691 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002692
Brian Salomon05441c42017-05-15 16:45:49 -04002693 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002694 // this is the normalized distance from the outer rectangle of this
2695 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002696 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002697
Brian Salomon289e3d82016-12-14 15:52:56 -05002698 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002699 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002700 }
jvanverth6a397612016-08-26 08:15:33 -07002701
Brian Salomon05441c42017-05-15 16:45:49 -04002702 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2703 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002704 for (int i = 0; i < primIndexCount; ++i) {
2705 *indices++ = primIndices[i] + currStartVertex;
2706 }
2707
Brian Salomon05441c42017-05-15 16:45:49 -04002708 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002709 }
2710
Chris Dalton3809bab2017-06-13 10:55:06 -06002711 GrMesh mesh(GrPrimitiveType::kTriangles);
Brian Salomon802cb312018-06-08 18:05:20 -04002712 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1,
2713 GrPrimitiveRestart::kNo);
Chris Dalton114a3c02017-05-26 15:17:19 -06002714 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon49348902018-06-26 09:12:38 -04002715 auto pipe = fHelper.makePipeline(target);
2716 target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002717 }
2718
Brian Salomon25a88092016-12-01 09:36:50 -05002719 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002720 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002721
2722 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002723 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002724 return false;
2725 }
2726
Brian Salomon05441c42017-05-15 16:45:49 -04002727 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002728 return false;
2729 }
2730
Brian Salomon05441c42017-05-15 16:45:49 -04002731 if (fHelper.usesLocalCoords() &&
2732 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002733 return false;
2734 }
2735
Brian Salomon05441c42017-05-15 16:45:49 -04002736 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002737 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002738 fVertCount += that->fVertCount;
2739 fIndexCount += that->fIndexCount;
2740 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002741 return true;
2742 }
2743
Brian Salomon05441c42017-05-15 16:45:49 -04002744 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002745 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002746 SkScalar fInnerRadius;
2747 SkScalar fOuterRadius;
2748 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002749 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002750 };
2751
Brian Salomon289e3d82016-12-14 15:52:56 -05002752 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002753 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002754 int fVertCount;
2755 int fIndexCount;
2756 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002757 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002758
Brian Salomon05441c42017-05-15 16:45:49 -04002759 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002760};
2761
jvanverth84839f62016-08-29 10:16:40 -07002762static const int kNumRRectsInIndexBuffer = 256;
2763
2764GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2765GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
Brian Salomond28a79d2017-10-16 13:01:07 -04002766static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
2767 GrResourceProvider* resourceProvider) {
jvanverth84839f62016-08-29 10:16:40 -07002768 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2769 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2770 switch (type) {
2771 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002772 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002773 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2774 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002775 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002776 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002777 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2778 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002779 default:
2780 SkASSERT(false);
2781 return nullptr;
2782 };
2783}
2784
Brian Salomon05441c42017-05-15 16:45:49 -04002785class EllipticalRRectOp : public GrMeshDrawOp {
2786private:
2787 using Helper = GrSimpleMeshDrawOpHelper;
2788
joshualitt76e7fb62015-02-11 08:52:27 -08002789public:
Brian Salomon25a88092016-12-01 09:36:50 -05002790 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002791
bsalomon4b4a7cc2016-07-08 04:42:54 -07002792 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2793 // whether the rrect is only stroked or stroked and filled.
Robert Phillips7c525e62018-06-12 10:11:12 -04002794 static std::unique_ptr<GrDrawOp> Make(GrContext* context,
2795 GrPaint&& paint,
2796 const SkMatrix& viewMatrix,
2797 const SkRect& devRect,
2798 float devXRadius,
2799 float devYRadius,
2800 SkVector devStrokeWidths,
2801 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002802 SkASSERT(devXRadius > 0.5);
2803 SkASSERT(devYRadius > 0.5);
2804 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2805 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002806 if (devStrokeWidths.fX > 0) {
2807 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2808 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2809 } else {
2810 devStrokeWidths.scale(SK_ScalarHalf);
2811 }
joshualitt76e7fb62015-02-11 08:52:27 -08002812
bsalomon4b4a7cc2016-07-08 04:42:54 -07002813 // we only handle thick strokes for near-circular ellipses
2814 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002815 (SK_ScalarHalf * devXRadius > devYRadius ||
2816 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002817 return nullptr;
2818 }
2819
2820 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002821 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2822 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002823 return nullptr;
2824 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002825 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2826 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002827 return nullptr;
2828 }
Brian Salomon05441c42017-05-15 16:45:49 -04002829 }
Robert Phillips7c525e62018-06-12 10:11:12 -04002830 return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
2831 viewMatrix, devRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002832 devXRadius, devYRadius, devStrokeWidths,
2833 strokeOnly);
2834 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002835
Brian Salomonea26d6b2018-01-23 20:33:21 +00002836 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2837 const SkRect& devRect, float devXRadius, float devYRadius,
2838 SkVector devStrokeHalfWidths, bool strokeOnly)
2839 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
Brian Salomon05441c42017-05-15 16:45:49 -04002840 SkScalar innerXRadius = 0.0f;
2841 SkScalar innerYRadius = 0.0f;
2842 SkRect bounds = devRect;
2843 bool stroked = false;
2844 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002845 // this is legit only if scale & translation (which should be the case at the moment)
2846 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002847 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2848 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002849 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2850 }
2851
Brian Salomon05441c42017-05-15 16:45:49 -04002852 devXRadius += devStrokeHalfWidths.fX;
2853 devYRadius += devStrokeHalfWidths.fY;
2854 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002855 }
2856
Brian Salomon05441c42017-05-15 16:45:49 -04002857 fStroked = stroked;
2858 fViewMatrixIfUsingLocalCoords = viewMatrix;
2859 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002860 // Expand the rect for aa in order to generate the correct vertices.
2861 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002862 fRRects.emplace_back(
2863 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002864 }
2865
Brian Salomon289e3d82016-12-14 15:52:56 -05002866 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002867
Robert Phillipsf1748f52017-09-14 14:11:24 -04002868 void visitProxies(const VisitProxyFunc& func) const override {
Robert Phillipsb493eeb2017-09-13 13:10:52 -04002869 fHelper.visitProxies(func);
2870 }
2871
Brian Salomon7c3e7182016-12-01 09:35:30 -05002872 SkString dumpInfo() const override {
2873 SkString string;
2874 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002875 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002876 string.appendf(
2877 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2878 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2879 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2880 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2881 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002882 }
Brian Salomon82dfd3d2017-06-14 12:30:35 -04002883 string += fHelper.dumpInfo();
2884 string += INHERITED::dumpInfo();
Brian Salomon7c3e7182016-12-01 09:35:30 -05002885 return string;
2886 }
2887
Brian Osman9a725dd2017-09-20 09:53:22 -04002888 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
2889 GrPixelConfigIsClamped dstIsClamped) override {
Brian Salomon05441c42017-05-15 16:45:49 -04002890 GrColor* color = &fRRects.front().fColor;
Brian Osman9a725dd2017-09-20 09:53:22 -04002891 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
2892 GrProcessorAnalysisCoverage::kSingleChannel, color);
Brian Salomon05441c42017-05-15 16:45:49 -04002893 }
2894
2895 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2896
bsalomone46f9fe2015-08-18 06:05:14 -07002897private:
Brian Salomon91326c32017-08-09 16:02:19 -04002898 void onPrepareDraws(Target* target) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002899 SkMatrix localMatrix;
2900 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002901 return;
2902 }
2903
2904 // Setup geometry processor
Brian Salomonea26d6b2018-01-23 20:33:21 +00002905 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002906
Brian Salomon92be2f72018-06-19 14:33:47 -04002907 SkASSERT(sizeof(EllipseVertex) == gp->debugOnly_vertexStride());
joshualitt76e7fb62015-02-11 08:52:27 -08002908
bsalomonb5238a72015-05-05 07:49:49 -07002909 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002910 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomond28a79d2017-10-16 13:01:07 -04002911 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
2912 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
joshualitt76e7fb62015-02-11 08:52:27 -08002913
Chris Dalton3809bab2017-06-13 10:55:06 -06002914 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002915 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon92be2f72018-06-19 14:33:47 -04002916 helper.init(target, sizeof(EllipseVertex), indexBuffer.get(),
2917 kVertsPerStandardRRect, indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002918 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002919 SkDebugf("Could not allocate vertices\n");
2920 return;
2921 }
2922
Brian Salomon05441c42017-05-15 16:45:49 -04002923 for (const auto& rrect : fRRects) {
2924 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002925 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002926 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2927 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2928 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2929 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002930
2931 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002932 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2933 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002934
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002935 SkScalar xMaxOffset = xOuterRadius;
2936 SkScalar yMaxOffset = yOuterRadius;
2937 if (!fStroked) {
2938 // For filled rrects we map a unit circle in the vertex attributes rather than
2939 // computing an ellipse and modifying that distance, so we normalize to 1.
2940 xMaxOffset /= rrect.fXRadius;
2941 yMaxOffset /= rrect.fYRadius;
2942 }
2943
Brian Salomon05441c42017-05-15 16:45:49 -04002944 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002945
Brian Salomon289e3d82016-12-14 15:52:56 -05002946 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2947 bounds.fBottom - yOuterRadius, bounds.fBottom};
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002948 SkScalar yOuterOffsets[4] = {yMaxOffset,
Brian Salomon289e3d82016-12-14 15:52:56 -05002949 SK_ScalarNearlyZero, // we're using inversesqrt() in
2950 // shader, so can't be exactly 0
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002951 SK_ScalarNearlyZero, yMaxOffset};
joshualitt76e7fb62015-02-11 08:52:27 -08002952
2953 for (int i = 0; i < 4; ++i) {
2954 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002955 verts->fColor = color;
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002956 verts->fOffset = SkPoint::Make(xMaxOffset, yOuterOffsets[i]);
joshualitt76e7fb62015-02-11 08:52:27 -08002957 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2958 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2959 verts++;
2960
2961 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002962 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002963 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2964 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2965 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2966 verts++;
2967
2968 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002969 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002970 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2971 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2972 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2973 verts++;
2974
2975 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002976 verts->fColor = color;
Jim Van Verthb1b87d92018-06-28 10:46:05 -04002977 verts->fOffset = SkPoint::Make(xMaxOffset, yOuterOffsets[i]);
joshualitt76e7fb62015-02-11 08:52:27 -08002978 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2979 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2980 verts++;
2981 }
2982 }
Brian Salomon49348902018-06-26 09:12:38 -04002983 auto pipe = fHelper.makePipeline(target);
2984 helper.recordDraw(target, gp.get(), pipe.fPipeline, pipe.fFixedDynamicState);
joshualitt76e7fb62015-02-11 08:52:27 -08002985 }
2986
Brian Salomon25a88092016-12-01 09:36:50 -05002987 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002988 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002989
Brian Salomon05441c42017-05-15 16:45:49 -04002990 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002991 return false;
2992 }
2993
bsalomoncdaa97b2016-03-08 08:30:14 -08002994 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002995 return false;
2996 }
2997
Brian Salomon05441c42017-05-15 16:45:49 -04002998 if (fHelper.usesLocalCoords() &&
2999 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08003000 return false;
3001 }
Brian Salomonea26d6b2018-01-23 20:33:21 +00003002
Brian Salomon05441c42017-05-15 16:45:49 -04003003 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07003004 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08003005 return true;
3006 }
3007
Brian Salomon05441c42017-05-15 16:45:49 -04003008 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003009 GrColor fColor;
3010 SkScalar fXRadius;
3011 SkScalar fYRadius;
3012 SkScalar fInnerXRadius;
3013 SkScalar fInnerYRadius;
3014 SkRect fDevBounds;
3015 };
3016
Brian Salomon289e3d82016-12-14 15:52:56 -05003017 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04003018 Helper fHelper;
3019 bool fStroked;
3020 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07003021
Brian Salomon05441c42017-05-15 16:45:49 -04003022 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08003023};
3024
Robert Phillips7c525e62018-06-12 10:11:12 -04003025static std::unique_ptr<GrDrawOp> make_rrect_op(GrContext* context,
3026 GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04003027 const SkMatrix& viewMatrix,
3028 const SkRRect& rrect,
3029 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07003030 SkASSERT(viewMatrix.rectStaysRect());
3031 SkASSERT(rrect.isSimple());
3032 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00003033
Brian Salomon53e4c3c2016-12-21 11:38:53 -05003034 // RRect ops only handle simple, but not too simple, rrects.
3035 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003036 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07003037 SkRect bounds;
3038 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003039
Mike Reed242135a2018-02-22 13:41:39 -05003040 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
Brian Salomon289e3d82016-12-14 15:52:56 -05003041 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
3042 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
3043 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
3044 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00003045
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003046 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003047
bsalomon4b4a7cc2016-07-08 04:42:54 -07003048 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
3049 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003050 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003051
Brian Salomon289e3d82016-12-14 15:52:56 -05003052 bool isStrokeOnly =
3053 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003054 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
3055
jvanverthc3d0e422016-08-25 08:12:35 -07003056 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003057 if (hasStroke) {
3058 if (SkStrokeRec::kHairline_Style == style) {
3059 scaledStroke.set(1, 1);
3060 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05003061 scaledStroke.fX = SkScalarAbs(
3062 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
3063 scaledStroke.fY = SkScalarAbs(
3064 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003065 }
3066
jvanverthc3d0e422016-08-25 08:12:35 -07003067 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
3068 // for non-circular rrects, if half of strokewidth is greater than radius,
3069 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05003070 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
3071 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003072 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00003073 }
3074 }
3075
3076 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
3077 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
3078 // patch will have fractional coverage. This only matters when the interior is actually filled.
3079 // We could consider falling back to rect rendering here, since a tiny radius is
3080 // indistinguishable from a square corner.
3081 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003082 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003083 }
3084
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003085 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07003086 if (isCircular) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003087 return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, xRadius,
3088 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05003089 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003090 } else {
Robert Phillips7c525e62018-06-12 10:11:12 -04003091 return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
3092 xRadius, yRadius, scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003093 }
joshualitt3e708c52015-04-30 13:49:27 -07003094}
3095
Robert Phillips7c525e62018-06-12 10:11:12 -04003096std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrContext* context,
3097 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003098 const SkMatrix& viewMatrix,
3099 const SkRRect& rrect,
3100 const SkStrokeRec& stroke,
3101 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08003102 if (rrect.isOval()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003103 return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
3104 GrStyle(stroke, nullptr), shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07003105 }
3106
3107 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08003108 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07003109 }
3110
Robert Phillips7c525e62018-06-12 10:11:12 -04003111 return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00003112}
joshualitt3e708c52015-04-30 13:49:27 -07003113
bsalomon4b4a7cc2016-07-08 04:42:54 -07003114///////////////////////////////////////////////////////////////////////////////
3115
Robert Phillips7c525e62018-06-12 10:11:12 -04003116std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrContext* context,
3117 GrPaint&& paint,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003118 const SkMatrix& viewMatrix,
3119 const SkRect& oval,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003120 const GrStyle& style,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003121 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07003122 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07003123 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04003124 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
3125 circle_stays_circle(viewMatrix)) {
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003126 auto r = width / 2.f;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003127 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003128 if (style.hasNonDashPathEffect()) {
3129 return nullptr;
3130 } else if (style.isDashed()) {
3131 if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
3132 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
3133 return nullptr;
3134 }
3135 auto onInterval = style.dashIntervals()[0];
3136 auto offInterval = style.dashIntervals()[1];
3137 if (offInterval == 0) {
3138 GrStyle strokeStyle(style.strokeRec(), nullptr);
Robert Phillips7c525e62018-06-12 10:11:12 -04003139 return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
3140 strokeStyle, shaderCaps);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003141 } else if (onInterval == 0) {
3142 // There is nothing to draw but we have no way to indicate that here.
3143 return nullptr;
3144 }
3145 auto angularOnInterval = onInterval / r;
3146 auto angularOffInterval = offInterval / r;
3147 auto phaseAngle = style.dashPhase() / r;
3148 // Currently this function doesn't accept ovals with different start angles, though
3149 // it could.
3150 static const SkScalar kStartAngle = 0.f;
Robert Phillips7c525e62018-06-12 10:11:12 -04003151 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003152 style.strokeRec().getWidth(), kStartAngle,
3153 angularOnInterval, angularOffInterval, phaseAngle);
3154 }
Robert Phillips7c525e62018-06-12 10:11:12 -04003155 return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003156 }
3157
3158 if (style.pathEffect()) {
3159 return nullptr;
bsalomon4b4a7cc2016-07-08 04:42:54 -07003160 }
3161
Stan Ilieveb868aa2017-02-21 11:06:16 -05003162 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07003163 if (viewMatrix.rectStaysRect()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003164 return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
bsalomon4b4a7cc2016-07-08 04:42:54 -07003165 }
3166
Stan Ilieveb868aa2017-02-21 11:06:16 -05003167 // Otherwise, if we have shader derivative support, render as device-independent
3168 if (shaderCaps->shaderDerivativeSupport()) {
Robert Phillips7c525e62018-06-12 10:11:12 -04003169 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
Stan Ilieveb868aa2017-02-21 11:06:16 -05003170 }
3171
bsalomon4b4a7cc2016-07-08 04:42:54 -07003172 return nullptr;
3173}
3174
3175///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07003176
Robert Phillips7c525e62018-06-12 10:11:12 -04003177std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrContext* context,
3178 GrPaint&& paint,
3179 const SkMatrix& viewMatrix,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003180 const SkRect& oval, SkScalar startAngle,
3181 SkScalar sweepAngle, bool useCenter,
3182 const GrStyle& style,
3183 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07003184 SkASSERT(!oval.isEmpty());
3185 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003186 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07003187 if (SkScalarAbs(sweepAngle) >= 360.f) {
3188 return nullptr;
3189 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07003190 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
3191 return nullptr;
3192 }
3193 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05003194 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
3195 useCenter};
Robert Phillips7c525e62018-06-12 10:11:12 -04003196 return CircleOp::Make(context, std::move(paint), viewMatrix,
3197 center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003198}
3199
3200///////////////////////////////////////////////////////////////////////////////
3201
Hal Canary6f6961e2017-01-31 13:50:44 -05003202#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07003203
Brian Salomon05441c42017-05-15 16:45:49 -04003204GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07003205 do {
bsalomoncadf75a2016-08-22 14:24:24 -07003206 SkScalar rotate = random->nextSScalar1() * 360.f;
3207 SkScalar translateX = random->nextSScalar1() * 1000.f;
3208 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003209 SkScalar scale;
3210 do {
3211 scale = random->nextSScalar1() * 100.f;
3212 } while (scale == 0);
bsalomoncadf75a2016-08-22 14:24:24 -07003213 SkMatrix viewMatrix;
3214 viewMatrix.setRotate(rotate);
3215 viewMatrix.postTranslate(translateX, translateY);
3216 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07003217 SkRect circle = GrTest::TestSquare(random);
3218 SkPoint center = {circle.centerX(), circle.centerY()};
3219 SkScalar radius = circle.width() / 2.f;
3220 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05003221 CircleOp::ArcParams arcParamsTmp;
3222 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003223 if (random->nextBool()) {
3224 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07003225 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
3226 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07003227 arcParams = &arcParamsTmp;
3228 }
Robert Phillips7c525e62018-06-12 10:11:12 -04003229 std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
3230 center, radius,
Brian Salomonea26d6b2018-01-23 20:33:21 +00003231 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05003232 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05003233 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07003234 }
3235 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07003236}
3237
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003238GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
3239 SkScalar rotate = random->nextSScalar1() * 360.f;
3240 SkScalar translateX = random->nextSScalar1() * 1000.f;
3241 SkScalar translateY = random->nextSScalar1() * 1000.f;
Brian Osman4462c042018-06-08 16:35:44 -04003242 SkScalar scale;
3243 do {
3244 scale = random->nextSScalar1() * 100.f;
3245 } while (scale == 0);
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003246 SkMatrix viewMatrix;
3247 viewMatrix.setRotate(rotate);
3248 viewMatrix.postTranslate(translateX, translateY);
3249 viewMatrix.postScale(scale, scale);
3250 SkRect circle = GrTest::TestSquare(random);
3251 SkPoint center = {circle.centerX(), circle.centerY()};
3252 SkScalar radius = circle.width() / 2.f;
3253 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
3254 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
3255 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
3256 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
3257 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
Robert Phillips7c525e62018-06-12 10:11:12 -04003258 return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
3259 center, radius, strokeWidth,
Brian Salomon62e4f3d2018-04-20 13:54:11 -04003260 startAngle, onAngle, offAngle, phase);
3261}
3262
Brian Salomon05441c42017-05-15 16:45:49 -04003263GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003264 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07003265 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003266 return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3267 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003268}
3269
Brian Salomon05441c42017-05-15 16:45:49 -04003270GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003271 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07003272 SkRect ellipse = GrTest::TestSquare(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003273 return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
3274 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003275}
3276
Brian Salomon05441c42017-05-15 16:45:49 -04003277GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07003278 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07003279 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips7c525e62018-06-12 10:11:12 -04003280 return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
3281 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07003282}
3283
3284#endif