blob: 15c2569334cc815458b1e08237e7bbace0a93c0f [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"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.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"
24#include "glsl/GrGLSLVertexShaderBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050025#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040026#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080027
commit-bot@chromium.org81312832013-03-22 18:34:09 +000028namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080029
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050031 SkPoint fPos;
32 GrColor fColor;
33 SkPoint fOffset;
34 SkPoint fOuterRadii;
35 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000036};
37
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000038struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050039 SkPoint fPos;
40 GrColor fColor;
41 SkPoint fOuterOffset;
42 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000043};
44
Brian Salomon289e3d82016-12-14 15:52:56 -050045static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000046}
47
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000048///////////////////////////////////////////////////////////////////////////////
49
50/**
bsalomonce1c8862014-12-15 07:11:22 -080051 * The output of this effect is a modulation of the input color and coverage for a circle. It
52 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080053 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080054 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec4f : (p.xy, outerRad, innerRad)
57 * p is the position in the normalized space.
58 * outerRad is the outerRadius in device space.
59 * innerRad is the innerRadius in normalized space (ignored if not stroking).
bsalomon4f3a0ca2016-08-22 13:14:26 -070060 * Additional clip planes are supported for rendering circular arcs. The additional planes are
61 * either intersected or unioned together. Up to three planes are supported (an initial plane,
62 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050063 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070064 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000065 */
66
bsalomoncdaa97b2016-03-08 08:30:14 -080067class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000068public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070069 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
70 const SkMatrix& localMatrix)
71 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080072 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070073 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
74 kHigh_GrSLPrecision);
75 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
Jim Van Verth6750e912016-12-19 14:45:19 -050076 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
77 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 if (clipPlane) {
79 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
80 } else {
81 fInClipPlane = nullptr;
82 }
83 if (isectPlane) {
84 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
85 } else {
86 fInIsectPlane = nullptr;
87 }
88 if (unionPlane) {
89 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInUnionPlane = nullptr;
92 }
bsalomoncdaa97b2016-03-08 08:30:14 -080093 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000094 }
95
Brian Salomond3b65972017-03-22 12:05:03 -040096 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000099
Brian Salomon94efbf52016-11-29 13:43:05 -0500100 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700101 GLSLProcessor::GenKey(*this, caps, b);
102 }
103
Brian Salomon94efbf52016-11-29 13:43:05 -0500104 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700105 return new GLSLProcessor();
106 }
107
108private:
egdaniel57d3b032015-11-13 11:57:27 -0800109 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800111 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112
Brian Salomon289e3d82016-12-14 15:52:56 -0500113 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800114 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800115 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800116 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800117 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700118 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800119
joshualittabb52a12015-01-13 15:02:10 -0800120 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800121 varyingHandler->emitAttributes(cgp);
Jim Van Verth6750e912016-12-19 14:45:19 -0500122 fragBuilder->codeAppend("highp vec4 circleEdge;");
123 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
124 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700125 if (cgp.fInClipPlane) {
126 fragBuilder->codeAppend("vec3 clipPlane;");
127 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
128 }
129 if (cgp.fInIsectPlane) {
130 SkASSERT(cgp.fInClipPlane);
131 fragBuilder->codeAppend("vec3 isectPlane;");
132 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
133 }
134 if (cgp.fInUnionPlane) {
135 SkASSERT(cgp.fInClipPlane);
136 fragBuilder->codeAppend("vec3 unionPlane;");
137 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
138 }
joshualittabb52a12015-01-13 15:02:10 -0800139
joshualittb8c241a2015-05-19 08:23:30 -0700140 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700141 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800142
joshualittabb52a12015-01-13 15:02:10 -0800143 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700144 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800145
146 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800147 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800148 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800149 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800150 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700151 cgp.fInPosition->fName,
152 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700153 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800154
Jim Van Verth6750e912016-12-19 14:45:19 -0500155 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700156 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
157 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800158 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500159 fragBuilder->codeAppend(
160 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700161 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800162 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000163 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000164
bsalomon4f3a0ca2016-08-22 13:14:26 -0700165 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500166 fragBuilder->codeAppend(
167 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
168 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700169 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500170 fragBuilder->codeAppend(
171 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
172 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700173 }
174 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500175 fragBuilder->codeAppend(
176 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
177 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700178 }
179 fragBuilder->codeAppend("edgeAlpha *= clip;");
180 }
egdaniel4ca2e602015-11-18 08:01:26 -0800181 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000182 }
183
robertphillips46d36f02015-01-18 08:14:14 -0800184 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500185 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700186 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800187 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700188 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500189 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500191 key |= cgp.fInClipPlane ? 0x04 : 0x0;
192 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
193 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700194 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195 }
196
bsalomona624bf32016-09-20 09:12:47 -0700197 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
198 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700199 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700200 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700201 }
202
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203 private:
egdaniele659a582015-11-13 09:55:43 -0800204 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205 };
206
Brian Salomon289e3d82016-12-14 15:52:56 -0500207 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800208 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800209 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800210 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700211 const Attribute* fInClipPlane;
212 const Attribute* fInIsectPlane;
213 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215
joshualittb0a8a372014-09-23 09:50:21 -0700216 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217
joshualitt249af152014-09-15 11:41:13 -0700218 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219};
220
bsalomoncdaa97b2016-03-08 08:30:14 -0800221GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000222
Hal Canary6f6961e2017-01-31 13:50:44 -0500223#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700224sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500225 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
226 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
227 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228}
Hal Canary6f6961e2017-01-31 13:50:44 -0500229#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
231///////////////////////////////////////////////////////////////////////////////
232
233/**
234 * 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 +0000235 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
236 * in both x and y directions.
237 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000238 * 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 +0000239 */
240
bsalomoncdaa97b2016-03-08 08:30:14 -0800241class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500243 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800244 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700245 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
246 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
247 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
248 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800249 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 }
251
Brian Salomond3b65972017-03-22 12:05:03 -0400252 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253
mtklein36352bf2015-03-25 18:17:31 -0700254 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800255
Brian Salomon94efbf52016-11-29 13:43:05 -0500256 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700257 GLSLProcessor::GenKey(*this, caps, b);
258 }
259
Brian Salomon94efbf52016-11-29 13:43:05 -0500260 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700261 return new GLSLProcessor();
262 }
263
264private:
egdaniel57d3b032015-11-13 11:57:27 -0800265 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800267 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
Brian Salomon289e3d82016-12-14 15:52:56 -0500269 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800270 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800271 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800272 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800273 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000274
joshualittabb52a12015-01-13 15:02:10 -0800275 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800276 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800277
egdaniel8dcdedc2015-11-11 06:27:20 -0800278 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800279 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800280 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700281 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000282
egdaniel8dcdedc2015-11-11 06:27:20 -0800283 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800284 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500285 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800286
cdalton85285412016-02-18 12:37:07 -0800287 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700288 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700289 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800290
joshualittabb52a12015-01-13 15:02:10 -0800291 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700292 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800293
294 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800295 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800296 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800297 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800298 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700299 egp.fInPosition->fName,
300 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700301 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800302
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000303 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800304 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
305 ellipseRadii.fsIn());
306 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
307 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
308 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700309
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000310 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800311 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
312 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800313 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000315 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800316 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500317 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800318 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500319 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
320 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800321 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
322 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000323 }
324
egdaniel4ca2e602015-11-18 08:01:26 -0800325 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000326 }
327
robertphillips46d36f02015-01-18 08:14:14 -0800328 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500329 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700330 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800331 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
332 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700333 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700334 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000335 }
336
bsalomona624bf32016-09-20 09:12:47 -0700337 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
338 FPCoordTransformIter&& transformIter) override {
339 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
340 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700341 }
342
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 private:
egdaniele659a582015-11-13 09:55:43 -0800344 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000345 };
346
joshualitt71c92602015-01-14 08:12:47 -0800347 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800348 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800349 const Attribute* fInEllipseOffset;
350 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700351 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 bool fStroke;
353
joshualittb0a8a372014-09-23 09:50:21 -0700354 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355
joshualitt249af152014-09-15 11:41:13 -0700356 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357};
358
bsalomoncdaa97b2016-03-08 08:30:14 -0800359GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360
Hal Canary6f6961e2017-01-31 13:50:44 -0500361#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700362sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
363 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500364 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365}
Hal Canary6f6961e2017-01-31 13:50:44 -0500366#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367
368///////////////////////////////////////////////////////////////////////////////
369
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000371 * 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 +0000372 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
373 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
374 * using differentials.
375 *
376 * The result is device-independent and can be used with any affine matrix.
377 */
378
bsalomoncdaa97b2016-03-08 08:30:14 -0800379enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000380
bsalomoncdaa97b2016-03-08 08:30:14 -0800381class DIEllipseGeometryProcessor : public GrGeometryProcessor {
382public:
383 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500384 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800385 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700386 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
387 kHigh_GrSLPrecision);
388 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
389 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
390 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800391 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000392 }
393
Brian Salomond3b65972017-03-22 12:05:03 -0400394 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395
mtklein36352bf2015-03-25 18:17:31 -0700396 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000397
Brian Salomon94efbf52016-11-29 13:43:05 -0500398 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700399 GLSLProcessor::GenKey(*this, caps, b);
400 }
halcanary9d524f22016-03-29 09:03:52 -0700401
Brian Salomon94efbf52016-11-29 13:43:05 -0500402 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700403 return new GLSLProcessor();
404 }
405
406private:
egdaniel57d3b032015-11-13 11:57:27 -0800407 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000408 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500409 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000410
joshualitt465283c2015-09-11 08:19:35 -0700411 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800412 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800413 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800414 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800415 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000416
joshualittabb52a12015-01-13 15:02:10 -0800417 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800418 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800419
egdaniel8dcdedc2015-11-11 06:27:20 -0800420 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800421 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500422 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700423
egdaniel8dcdedc2015-11-11 06:27:20 -0800424 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800425 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500426 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800427
cdalton85285412016-02-18 12:37:07 -0800428 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700429 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800430
joshualittabb52a12015-01-13 15:02:10 -0800431 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800432 this->setupPosition(vertBuilder,
433 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800434 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700435 diegp.fInPosition->fName,
436 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700437 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800438
439 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800440 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800441 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800442 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800443 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700444 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700445 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800446
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000447 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800448 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
449 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
450 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
451 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500452 fragBuilder->codeAppendf(
453 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
454 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
455 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000456
egdaniel4ca2e602015-11-18 08:01:26 -0800457 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000458 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800459 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
460 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800461 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800463 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
464 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800466 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 }
468
469 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800470 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800471 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
472 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
473 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
474 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500475 fragBuilder->codeAppendf(
476 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
477 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
478 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800479 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
480 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481 }
482
egdaniel4ca2e602015-11-18 08:01:26 -0800483 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000484 }
485
robertphillips46d36f02015-01-18 08:14:14 -0800486 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500487 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700488 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800489 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
490 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700491 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700492 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000493 }
494
bsalomona624bf32016-09-20 09:12:47 -0700495 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
496 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800497 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700498
bsalomon31df31c2016-08-17 09:00:24 -0700499 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
500 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700501 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800502 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700503 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
504 }
bsalomona624bf32016-09-20 09:12:47 -0700505 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506 }
507
508 private:
joshualitt5559ca22015-05-21 15:50:36 -0700509 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700510 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800511
egdaniele659a582015-11-13 09:55:43 -0800512 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 };
514
joshualitt71c92602015-01-14 08:12:47 -0800515 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800516 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800517 const Attribute* fInEllipseOffsets0;
518 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500519 SkMatrix fViewMatrix;
520 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000521
joshualittb0a8a372014-09-23 09:50:21 -0700522 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000523
joshualitt249af152014-09-15 11:41:13 -0700524 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000525};
526
bsalomoncdaa97b2016-03-08 08:30:14 -0800527GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000528
Hal Canary6f6961e2017-01-31 13:50:44 -0500529#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700530sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500531 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
532 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533}
Hal Canary6f6961e2017-01-31 13:50:44 -0500534#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000535
536///////////////////////////////////////////////////////////////////////////////
537
jvanverth6ca48822016-10-07 06:57:32 -0700538// We have two possible cases for geometry for a circle:
539
540// In the case of a normal fill, we draw geometry for the circle as an octagon.
541static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500542 // enter the octagon
543 // clang-format off
544 0, 1, 8, 1, 2, 8,
545 2, 3, 8, 3, 4, 8,
546 4, 5, 8, 5, 6, 8,
547 6, 7, 8, 7, 0, 8
548 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700549};
550
551// For stroked circles, we use two nested octagons.
552static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500553 // enter the octagon
554 // clang-format off
555 0, 1, 9, 0, 9, 8,
556 1, 2, 10, 1, 10, 9,
557 2, 3, 11, 2, 11, 10,
558 3, 4, 12, 3, 12, 11,
559 4, 5, 13, 4, 13, 12,
560 5, 6, 14, 5, 14, 13,
561 6, 7, 15, 6, 15, 14,
562 7, 0, 8, 7, 8, 15,
563 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700564};
565
Brian Salomon289e3d82016-12-14 15:52:56 -0500566
jvanverth6ca48822016-10-07 06:57:32 -0700567static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
568static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
569static const int kVertsPerStrokeCircle = 16;
570static const int kVertsPerFillCircle = 9;
571
572static int circle_type_to_vert_count(bool stroked) {
573 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
574}
575
576static int circle_type_to_index_count(bool stroked) {
577 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
578}
579
580static const uint16_t* circle_type_to_indices(bool stroked) {
581 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
582}
583
584///////////////////////////////////////////////////////////////////////////////
585
Brian Salomon05441c42017-05-15 16:45:49 -0400586class CircleOp final : public GrMeshDrawOp {
587private:
588 using Helper = GrSimpleMeshDrawOpHelper;
589
joshualitt76e7fb62015-02-11 08:52:27 -0800590public:
Brian Salomon25a88092016-12-01 09:36:50 -0500591 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700592
bsalomon4f3a0ca2016-08-22 13:14:26 -0700593 /** Optional extra params to render a partial arc rather than a full circle. */
594 struct ArcParams {
595 SkScalar fStartAngleRadians;
596 SkScalar fSweepAngleRadians;
597 bool fUseCenter;
598 };
Brian Salomon05441c42017-05-15 16:45:49 -0400599
600 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
601 SkPoint center, SkScalar radius, const GrStyle& style,
602 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700603 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700604 if (style.hasPathEffect()) {
605 return nullptr;
606 }
Brian Salomon05441c42017-05-15 16:45:49 -0400607 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700608 SkStrokeRec::Style recStyle = stroke.getStyle();
609 if (arcParams) {
610 // Arc support depends on the style.
611 switch (recStyle) {
612 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500613 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700614 return nullptr;
615 case SkStrokeRec::kFill_Style:
616 // This supports all fills.
617 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500618 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700619 case SkStrokeRec::kHairline_Style:
620 // Strokes that don't use the center point are supported with butt cap.
621 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
622 return nullptr;
623 }
624 break;
625 }
626 }
Brian Salomon05441c42017-05-15 16:45:49 -0400627 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
628 arcParams);
629 }
630
631 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
632 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
633 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
634 const SkStrokeRec& stroke = style.strokeRec();
635 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700636
bsalomon4b4a7cc2016-07-08 04:42:54 -0700637 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700638 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800640
Brian Salomon289e3d82016-12-14 15:52:56 -0500641 bool isStrokeOnly =
642 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700643 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700644
jvanverth6ca48822016-10-07 06:57:32 -0700645 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700646 SkScalar outerRadius = radius;
647 SkScalar halfWidth = 0;
648 if (hasStroke) {
649 if (SkScalarNearlyZero(strokeWidth)) {
650 halfWidth = SK_ScalarHalf;
651 } else {
652 halfWidth = SkScalarHalf(strokeWidth);
653 }
654
655 outerRadius += halfWidth;
656 if (isStrokeOnly) {
657 innerRadius = radius - halfWidth;
658 }
659 }
660
661 // The radii are outset for two reasons. First, it allows the shader to simply perform
662 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
663 // Second, the outer radius is used to compute the verts of the bounding box that is
664 // rendered and the outset ensures the box will cover all partially covered by the circle.
665 outerRadius += SK_ScalarHalf;
666 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700667 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400668 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700669
bsalomon4f3a0ca2016-08-22 13:14:26 -0700670 // This makes every point fully inside the intersection plane.
671 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
672 // This makes every point fully outside the union plane.
673 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
674 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
675 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700676 if (arcParams) {
677 // The shader operates in a space where the circle is translated to be centered at the
678 // origin. Here we compute points on the unit circle at the starting and ending angles.
679 SkPoint startPoint, stopPoint;
680 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
681 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
682 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400683
684 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
685 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
686 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
687 startPoint.normalize();
688 stopPoint.normalize();
689
690 // If the matrix included scale (on one axis) we need to swap our start and end points
691 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
692 SkTSwap(startPoint, stopPoint);
693 }
694
bsalomon4f3a0ca2016-08-22 13:14:26 -0700695 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
696 // radial lines. However, in both cases we have to be careful about the half-circle.
697 // case. In that case the two radial lines are equal and so that edge gets clipped
698 // twice. Since the shared edge goes through the center we fall back on the useCenter
699 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500700 bool useCenter =
701 (arcParams->fUseCenter || isStrokeOnly) &&
702 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700703 if (useCenter) {
704 SkVector norm0 = {startPoint.fY, -startPoint.fX};
705 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
706 if (arcParams->fSweepAngleRadians > 0) {
707 norm0.negate();
708 } else {
709 norm1.negate();
710 }
Brian Salomon05441c42017-05-15 16:45:49 -0400711 fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700712 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -0400713 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700714 color,
715 innerRadius,
716 outerRadius,
717 {norm0.fX, norm0.fY, 0.5f},
718 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
719 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700720 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500721 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400722 fClipPlaneIsect = false;
723 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700724 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400725 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 color,
727 innerRadius,
728 outerRadius,
729 {norm0.fX, norm0.fY, 0.5f},
730 {norm1.fX, norm1.fY, 0.5f},
731 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700732 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500733 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400734 fClipPlaneIsect = true;
735 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700736 }
737 } else {
738 // We clip to a secant of the original circle.
739 startPoint.scale(radius);
740 stopPoint.scale(radius);
741 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
742 norm.normalize();
743 if (arcParams->fSweepAngleRadians > 0) {
744 norm.negate();
745 }
746 SkScalar d = -norm.dot(startPoint) + 0.5f;
747
Brian Salomon05441c42017-05-15 16:45:49 -0400748 fCircles.emplace_back(
749 Circle{color,
750 innerRadius,
751 outerRadius,
752 {norm.fX, norm.fY, d},
753 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
754 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
755 devBounds,
756 stroked});
757 fClipPlane = true;
758 fClipPlaneIsect = false;
759 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700760 }
761 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400762 fCircles.emplace_back(
763 Circle{color,
764 innerRadius,
765 outerRadius,
766 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
767 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
768 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
769 devBounds,
770 stroked});
771 fClipPlane = false;
772 fClipPlaneIsect = false;
773 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700774 }
bsalomon88cf17d2016-07-08 06:40:56 -0700775 // Use the original radius and stroke radius for the bounds so that it does not include the
776 // AA bloat.
777 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -0400778 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -0500779 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
780 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -0400781 fVertCount = circle_type_to_vert_count(stroked);
782 fIndexCount = circle_type_to_index_count(stroked);
783 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -0800784 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700785
Brian Salomon289e3d82016-12-14 15:52:56 -0500786 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800787
robertphillipse004bfc2015-11-16 09:06:59 -0800788 SkString dumpInfo() const override {
789 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -0400790 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500791 string.appendf(
792 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
793 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -0400794 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
795 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
796 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800797 }
798 string.append(INHERITED::dumpInfo());
799 return string;
800 }
801
Brian Salomon05441c42017-05-15 16:45:49 -0400802 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
803 GrColor* color = &fCircles.front().fColor;
804 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
805 color);
806 }
807
808 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
809
bsalomone46f9fe2015-08-18 06:05:14 -0700810private:
joshualitt144c3c82015-11-30 12:30:13 -0800811 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800812 SkMatrix localMatrix;
813 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800814 return;
815 }
816
817 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500818 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
819 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700820
821 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500822 SkPoint fPos;
823 GrColor fColor;
824 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700825 SkScalar fOuterRadius;
826 SkScalar fInnerRadius;
827 // These planes may or may not be present in the vertex buffer.
828 SkScalar fHalfPlanes[3][3];
829 };
joshualitt76e7fb62015-02-11 08:52:27 -0800830
joshualitt76e7fb62015-02-11 08:52:27 -0800831 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500832 SkASSERT(vertexStride ==
833 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
834 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
835 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700836
837 const GrBuffer* vertexBuffer;
838 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500839 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
840 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700841 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700842 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800843 return;
844 }
845
jvanverth6ca48822016-10-07 06:57:32 -0700846 const GrBuffer* indexBuffer = nullptr;
847 int firstIndex = 0;
848 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
849 if (!indices) {
850 SkDebugf("Could not allocate indices\n");
851 return;
852 }
853
854 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -0400855 for (const auto& circle : fCircles) {
856 SkScalar innerRadius = circle.fInnerRadius;
857 SkScalar outerRadius = circle.fOuterRadius;
858 GrColor color = circle.fColor;
859 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800860
Brian Salomon289e3d82016-12-14 15:52:56 -0500861 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
862 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
863 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
864 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
865 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
866 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
867 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
868 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800869
870 // The inner radius in the vertex data must be specified in normalized space.
871 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700872
873 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500874 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700875 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500876
Brian Salomon289e3d82016-12-14 15:52:56 -0500877 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700878 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700879 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700880 v0->fOuterRadius = outerRadius;
881 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800882
Brian Salomon289e3d82016-12-14 15:52:56 -0500883 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700884 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700885 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700886 v1->fOuterRadius = outerRadius;
887 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800888
Brian Salomon289e3d82016-12-14 15:52:56 -0500889 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700890 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700891 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700892 v2->fOuterRadius = outerRadius;
893 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800894
Brian Salomon289e3d82016-12-14 15:52:56 -0500895 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700896 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700897 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 v3->fOuterRadius = outerRadius;
899 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800900
Brian Salomon289e3d82016-12-14 15:52:56 -0500901 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700902 v4->fColor = color;
903 v4->fOffset = SkPoint::Make(octOffset, 1);
904 v4->fOuterRadius = outerRadius;
905 v4->fInnerRadius = innerRadius;
906
Brian Salomon289e3d82016-12-14 15:52:56 -0500907 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700908 v5->fColor = color;
909 v5->fOffset = SkPoint::Make(-octOffset, 1);
910 v5->fOuterRadius = outerRadius;
911 v5->fInnerRadius = innerRadius;
912
Brian Salomon289e3d82016-12-14 15:52:56 -0500913 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700914 v6->fColor = color;
915 v6->fOffset = SkPoint::Make(-1, octOffset);
916 v6->fOuterRadius = outerRadius;
917 v6->fInnerRadius = innerRadius;
918
Brian Salomon289e3d82016-12-14 15:52:56 -0500919 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700920 v7->fColor = color;
921 v7->fOffset = SkPoint::Make(-1, -octOffset);
922 v7->fOuterRadius = outerRadius;
923 v7->fInnerRadius = innerRadius;
924
bsalomon4f3a0ca2016-08-22 13:14:26 -0700925 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -0400926 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
927 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
928 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
929 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
930 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
931 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
932 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
933 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700934 }
935 int unionIdx = 1;
936 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -0400937 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
938 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
939 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
940 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
941 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
942 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
943 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
944 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700945 unionIdx = 2;
946 }
947 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -0400948 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
949 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
950 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
951 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
952 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
953 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
954 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
955 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700956 }
jvanverth6ca48822016-10-07 06:57:32 -0700957
Brian Salomon05441c42017-05-15 16:45:49 -0400958 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -0700959 // compute the inner ring
960 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
961 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
962 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
963 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
964 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
965 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
966 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
967 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
968
969 // cosine and sine of pi/8
970 SkScalar c = 0.923579533f;
971 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -0400972 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700973
Brian Salomon289e3d82016-12-14 15:52:56 -0500974 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700975 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500976 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700977 v0->fOuterRadius = outerRadius;
978 v0->fInnerRadius = innerRadius;
979
Brian Salomon289e3d82016-12-14 15:52:56 -0500980 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700981 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500982 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700983 v1->fOuterRadius = outerRadius;
984 v1->fInnerRadius = innerRadius;
985
Brian Salomon289e3d82016-12-14 15:52:56 -0500986 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700987 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500988 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700989 v2->fOuterRadius = outerRadius;
990 v2->fInnerRadius = innerRadius;
991
Brian Salomon289e3d82016-12-14 15:52:56 -0500992 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700993 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500994 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700995 v3->fOuterRadius = outerRadius;
996 v3->fInnerRadius = innerRadius;
997
Brian Salomon289e3d82016-12-14 15:52:56 -0500998 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700999 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001000 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001001 v4->fOuterRadius = outerRadius;
1002 v4->fInnerRadius = innerRadius;
1003
Brian Salomon289e3d82016-12-14 15:52:56 -05001004 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001005 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001006 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001007 v5->fOuterRadius = outerRadius;
1008 v5->fInnerRadius = innerRadius;
1009
Brian Salomon289e3d82016-12-14 15:52:56 -05001010 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001011 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001012 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001013 v6->fOuterRadius = outerRadius;
1014 v6->fInnerRadius = innerRadius;
1015
Brian Salomon289e3d82016-12-14 15:52:56 -05001016 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001017 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001018 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001019 v7->fOuterRadius = outerRadius;
1020 v7->fInnerRadius = innerRadius;
1021
1022 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001023 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1024 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1025 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1026 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1027 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1028 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1029 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1030 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001031 }
1032 int unionIdx = 1;
1033 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001034 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1035 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1036 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1037 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1038 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1039 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1040 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1041 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001042 unionIdx = 2;
1043 }
1044 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001045 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1046 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1047 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1048 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1049 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1050 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1051 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1052 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001053 }
1054 } else {
1055 // filled
1056 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1057 v8->fPos = center;
1058 v8->fColor = color;
1059 v8->fOffset = SkPoint::Make(0, 0);
1060 v8->fOuterRadius = outerRadius;
1061 v8->fInnerRadius = innerRadius;
1062 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001063 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001064 }
1065 int unionIdx = 1;
1066 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001067 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001068 unionIdx = 2;
1069 }
1070 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001071 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001072 }
1073 }
1074
Brian Salomon05441c42017-05-15 16:45:49 -04001075 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1076 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001077 for (int i = 0; i < primIndexCount; ++i) {
1078 *indices++ = primIndices[i] + currStartVertex;
1079 }
1080
Brian Salomon05441c42017-05-15 16:45:49 -04001081 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1082 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001083 }
jvanverth6ca48822016-10-07 06:57:32 -07001084
Chris Dalton3809bab2017-06-13 10:55:06 -06001085 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001086 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1087 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001088 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001089 }
1090
Brian Salomon25a88092016-12-01 09:36:50 -05001091 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001092 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001093
1094 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001095 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001096 return false;
1097 }
1098
Brian Salomon05441c42017-05-15 16:45:49 -04001099 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001100 return false;
1101 }
1102
Brian Salomon05441c42017-05-15 16:45:49 -04001103 if (fHelper.usesLocalCoords() &&
1104 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001105 return false;
1106 }
1107
Brian Salomon289e3d82016-12-14 15:52:56 -05001108 // Because we've set up the ops that don't use the planes with noop values
1109 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 fClipPlane |= that->fClipPlane;
1111 fClipPlaneIsect |= that->fClipPlaneIsect;
1112 fClipPlaneUnion |= that->fClipPlaneUnion;
1113
Brian Salomon05441c42017-05-15 16:45:49 -04001114 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001115 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001116 fVertCount += that->fVertCount;
1117 fIndexCount += that->fIndexCount;
1118 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001119 return true;
1120 }
1121
Brian Salomon05441c42017-05-15 16:45:49 -04001122 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001123 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001124 SkScalar fInnerRadius;
1125 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 SkScalar fClipPlane[3];
1127 SkScalar fIsectPlane[3];
1128 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001129 SkRect fDevBounds;
1130 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001131 };
1132
Brian Salomon289e3d82016-12-14 15:52:56 -05001133 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001134 Helper fHelper;
1135 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001136 int fVertCount;
1137 int fIndexCount;
1138 bool fAllFill;
1139 bool fClipPlane;
1140 bool fClipPlaneIsect;
1141 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001142
Brian Salomon05441c42017-05-15 16:45:49 -04001143 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001144};
1145
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001146///////////////////////////////////////////////////////////////////////////////
1147
Brian Salomon05441c42017-05-15 16:45:49 -04001148class EllipseOp : public GrMeshDrawOp {
1149private:
1150 using Helper = GrSimpleMeshDrawOpHelper;
1151
1152 struct DeviceSpaceParams {
1153 SkPoint fCenter;
1154 SkScalar fXRadius;
1155 SkScalar fYRadius;
1156 SkScalar fInnerXRadius;
1157 SkScalar fInnerYRadius;
1158 };
1159
joshualitt76e7fb62015-02-11 08:52:27 -08001160public:
Brian Salomon25a88092016-12-01 09:36:50 -05001161 DEFINE_OP_CLASS_ID
Brian Salomon05441c42017-05-15 16:45:49 -04001162 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1163 const SkRect& ellipse, const SkStrokeRec& stroke) {
1164 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001165 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001166 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1167 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001168 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1169 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001170 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1171 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1172 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1173 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001174
bsalomon4b4a7cc2016-07-08 04:42:54 -07001175 // do (potentially) anisotropic mapping of stroke
1176 SkVector scaledStroke;
1177 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001178 scaledStroke.fX = SkScalarAbs(
1179 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1180 scaledStroke.fY = SkScalarAbs(
1181 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001182
1183 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001184 bool isStrokeOnly =
1185 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001186 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1187
Brian Salomon05441c42017-05-15 16:45:49 -04001188 params.fInnerXRadius = 0;
1189 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001190 if (hasStroke) {
1191 if (SkScalarNearlyZero(scaledStroke.length())) {
1192 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1193 } else {
1194 scaledStroke.scale(SK_ScalarHalf);
1195 }
1196
1197 // we only handle thick strokes for near-circular ellipses
1198 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001199 (0.5f * params.fXRadius > params.fYRadius ||
1200 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001201 return nullptr;
1202 }
1203
1204 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001205 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1206 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1207 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1208 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001209 return nullptr;
1210 }
1211
1212 // this is legit only if scale & translation (which should be the case at the moment)
1213 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001214 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1215 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001216 }
1217
Brian Salomon05441c42017-05-15 16:45:49 -04001218 params.fXRadius += scaledStroke.fX;
1219 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001220 }
Brian Salomon05441c42017-05-15 16:45:49 -04001221 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1222 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001223
Brian Salomon05441c42017-05-15 16:45:49 -04001224 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1225 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1226 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1227 SkStrokeRec::Style style = stroke.getStyle();
1228 bool isStrokeOnly =
1229 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001230
Brian Salomon05441c42017-05-15 16:45:49 -04001231 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1232 params.fInnerXRadius, params.fInnerYRadius,
1233 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1234 params.fCenter.fY - params.fYRadius,
1235 params.fCenter.fX + params.fXRadius,
1236 params.fCenter.fY + params.fYRadius)});
1237
1238 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001239
bsalomon4b4a7cc2016-07-08 04:42:54 -07001240 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001241 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001242
Brian Salomon05441c42017-05-15 16:45:49 -04001243 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1244 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001245 }
joshualitt76e7fb62015-02-11 08:52:27 -08001246
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001248
Brian Salomon7c3e7182016-12-01 09:35:30 -05001249 SkString dumpInfo() const override {
1250 SkString string;
1251 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001252 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001253 string.appendf(
1254 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1255 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1256 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1257 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1258 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001259 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001260 string.append(INHERITED::dumpInfo());
1261 return string;
1262 }
1263
Brian Salomon05441c42017-05-15 16:45:49 -04001264 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1265 GrColor* color = &fEllipses.front().fColor;
1266 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1267 color);
1268 }
1269
1270 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1271
bsalomone46f9fe2015-08-18 06:05:14 -07001272private:
joshualitt144c3c82015-11-30 12:30:13 -08001273 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001274 SkMatrix localMatrix;
1275 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001276 return;
1277 }
1278
1279 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001280 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001281
bsalomonb5238a72015-05-05 07:49:49 -07001282 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001283 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001284 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001285 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1286 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001287 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001288 return;
1289 }
1290
Brian Salomon05441c42017-05-15 16:45:49 -04001291 for (const auto& ellipse : fEllipses) {
1292 GrColor color = ellipse.fColor;
1293 SkScalar xRadius = ellipse.fXRadius;
1294 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001295
1296 // Compute the reciprocals of the radii here to save time in the shader
1297 SkScalar xRadRecip = SkScalarInvert(xRadius);
1298 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001299 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1300 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001301
vjiaoblack977996d2016-06-30 12:20:54 -07001302 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1303 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1304 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1305
joshualitt76e7fb62015-02-11 08:52:27 -08001306 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001307 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001308 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001309 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001310 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1311 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1312
Brian Salomon05441c42017-05-15 16:45:49 -04001313 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001314 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001315 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001316 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1317 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1318
Brian Salomon05441c42017-05-15 16:45:49 -04001319 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001320 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001321 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001322 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1323 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1324
Brian Salomon05441c42017-05-15 16:45:49 -04001325 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001326 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001327 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001328 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1329 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1330
bsalomonb5238a72015-05-05 07:49:49 -07001331 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001332 }
Brian Salomon05441c42017-05-15 16:45:49 -04001333 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001334 }
1335
Brian Salomon25a88092016-12-01 09:36:50 -05001336 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001337 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001338
Brian Salomon05441c42017-05-15 16:45:49 -04001339 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001340 return false;
1341 }
1342
bsalomoncdaa97b2016-03-08 08:30:14 -08001343 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001344 return false;
1345 }
1346
Brian Salomon05441c42017-05-15 16:45:49 -04001347 if (fHelper.usesLocalCoords() &&
1348 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001349 return false;
1350 }
1351
Brian Salomon05441c42017-05-15 16:45:49 -04001352 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001353 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001354 return true;
1355 }
1356
Brian Salomon05441c42017-05-15 16:45:49 -04001357 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001358 GrColor fColor;
1359 SkScalar fXRadius;
1360 SkScalar fYRadius;
1361 SkScalar fInnerXRadius;
1362 SkScalar fInnerYRadius;
1363 SkRect fDevBounds;
1364 };
joshualitt76e7fb62015-02-11 08:52:27 -08001365
Brian Salomon289e3d82016-12-14 15:52:56 -05001366 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001367 Helper fHelper;
1368 bool fStroked;
1369 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001370
Brian Salomon05441c42017-05-15 16:45:49 -04001371 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001372};
1373
joshualitt76e7fb62015-02-11 08:52:27 -08001374/////////////////////////////////////////////////////////////////////////////////////////////////
1375
Brian Salomon05441c42017-05-15 16:45:49 -04001376class DIEllipseOp : public GrMeshDrawOp {
1377private:
1378 using Helper = GrSimpleMeshDrawOpHelper;
1379
1380 struct DeviceSpaceParams {
1381 SkPoint fCenter;
1382 SkScalar fXRadius;
1383 SkScalar fYRadius;
1384 SkScalar fInnerXRadius;
1385 SkScalar fInnerYRadius;
1386 DIEllipseStyle fStyle;
1387 };
1388
joshualitt76e7fb62015-02-11 08:52:27 -08001389public:
Brian Salomon25a88092016-12-01 09:36:50 -05001390 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001391
Brian Salomon05441c42017-05-15 16:45:49 -04001392 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1393 const SkRect& ellipse, const SkStrokeRec& stroke) {
1394 DeviceSpaceParams params;
1395 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1396 params.fXRadius = SkScalarHalf(ellipse.width());
1397 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001398
bsalomon4b4a7cc2016-07-08 04:42:54 -07001399 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001400 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1401 ? DIEllipseStyle::kStroke
1402 : (SkStrokeRec::kHairline_Style == style)
1403 ? DIEllipseStyle::kHairline
1404 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001405
Brian Salomon05441c42017-05-15 16:45:49 -04001406 params.fInnerXRadius = 0;
1407 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001408 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1409 SkScalar strokeWidth = stroke.getWidth();
1410
1411 if (SkScalarNearlyZero(strokeWidth)) {
1412 strokeWidth = SK_ScalarHalf;
1413 } else {
1414 strokeWidth *= SK_ScalarHalf;
1415 }
1416
1417 // we only handle thick strokes for near-circular ellipses
1418 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001419 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1420 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001421 return nullptr;
1422 }
1423
1424 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001425 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1426 (strokeWidth * strokeWidth) * params.fXRadius) {
1427 return nullptr;
1428 }
1429 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1430 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001431 return nullptr;
1432 }
1433
1434 // set inner radius (if needed)
1435 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001436 params.fInnerXRadius = params.fXRadius - strokeWidth;
1437 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001438 }
1439
Brian Salomon05441c42017-05-15 16:45:49 -04001440 params.fXRadius += strokeWidth;
1441 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001442 }
Brian Salomon05441c42017-05-15 16:45:49 -04001443 if (DIEllipseStyle::kStroke == params.fStyle &&
1444 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1445 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001446 }
Brian Salomon05441c42017-05-15 16:45:49 -04001447 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1448 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001449
Brian Salomon05441c42017-05-15 16:45:49 -04001450 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1451 const SkMatrix& viewMatrix)
1452 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001453 // This expands the outer rect so that after CTM we end up with a half-pixel border
1454 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1455 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1456 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1457 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001458 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1459 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001460
Brian Salomon05441c42017-05-15 16:45:49 -04001461 fEllipses.emplace_back(
1462 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1463 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1464 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1465 params.fCenter.fY - params.fYRadius - geoDy,
1466 params.fCenter.fX + params.fXRadius + geoDx,
1467 params.fCenter.fY + params.fYRadius + geoDy)});
1468 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1469 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08001470 }
1471
Brian Salomon289e3d82016-12-14 15:52:56 -05001472 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001473
Brian Salomon7c3e7182016-12-01 09:35:30 -05001474 SkString dumpInfo() const override {
1475 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001476 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001477 string.appendf(
1478 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1479 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1480 "GeoDY: %.2f\n",
1481 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1482 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1483 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001484 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001485 string.append(INHERITED::dumpInfo());
1486 return string;
1487 }
1488
Brian Salomon05441c42017-05-15 16:45:49 -04001489 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1490 GrColor* color = &fEllipses.front().fColor;
1491 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1492 color);
1493 }
1494
1495 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1496
bsalomone46f9fe2015-08-18 06:05:14 -07001497private:
joshualitt144c3c82015-11-30 12:30:13 -08001498 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001499 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001500 sk_sp<GrGeometryProcessor> gp(
1501 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001502
joshualitt76e7fb62015-02-11 08:52:27 -08001503 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001504 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001505 QuadHelper helper;
1506 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04001507 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001508 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001509 return;
1510 }
1511
Brian Salomon05441c42017-05-15 16:45:49 -04001512 for (const auto& ellipse : fEllipses) {
1513 GrColor color = ellipse.fColor;
1514 SkScalar xRadius = ellipse.fXRadius;
1515 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001516
Brian Salomon05441c42017-05-15 16:45:49 -04001517 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001518
1519 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04001520 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1521 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001522
Brian Salomon05441c42017-05-15 16:45:49 -04001523 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1524 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001525
1526 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001527 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001528 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1529 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1530
Brian Salomon289e3d82016-12-14 15:52:56 -05001531 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001532 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001533 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1534 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1535
1536 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001537 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001538 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1539 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1540
1541 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001542 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001543 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1544 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1545
bsalomonb5238a72015-05-05 07:49:49 -07001546 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001547 }
Brian Salomon05441c42017-05-15 16:45:49 -04001548 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001549 }
halcanary9d524f22016-03-29 09:03:52 -07001550
Brian Salomon25a88092016-12-01 09:36:50 -05001551 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001552 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04001553 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001554 return false;
1555 }
1556
bsalomoncdaa97b2016-03-08 08:30:14 -08001557 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001558 return false;
1559 }
1560
joshualittd96a67b2015-05-05 14:09:05 -07001561 // TODO rewrite to allow positioning on CPU
1562 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001563 return false;
1564 }
1565
Brian Salomon05441c42017-05-15 16:45:49 -04001566 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001567 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001568 return true;
1569 }
1570
Brian Salomon05441c42017-05-15 16:45:49 -04001571 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1572 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001573
Brian Salomon05441c42017-05-15 16:45:49 -04001574 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001575 SkMatrix fViewMatrix;
1576 GrColor fColor;
1577 SkScalar fXRadius;
1578 SkScalar fYRadius;
1579 SkScalar fInnerXRadius;
1580 SkScalar fInnerYRadius;
1581 SkScalar fGeoDx;
1582 SkScalar fGeoDy;
1583 DIEllipseStyle fStyle;
1584 SkRect fBounds;
1585 };
1586
Brian Salomon05441c42017-05-15 16:45:49 -04001587 Helper fHelper;
1588 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001589
Brian Salomon05441c42017-05-15 16:45:49 -04001590 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001591};
1592
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001593///////////////////////////////////////////////////////////////////////////////
1594
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001595// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001596//
1597// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1598// ____________
1599// |_|________|_|
1600// | | | |
1601// | | | |
1602// | | | |
1603// |_|________|_|
1604// |_|________|_|
1605//
1606// For strokes, we don't draw the center quad.
1607//
1608// For circular roundrects, in the case where the stroke width is greater than twice
1609// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001610// in the center. The shared vertices are duplicated so we can set a different outer radius
1611// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001612// ____________
1613// |_|________|_|
1614// | |\ ____ /| |
1615// | | | | | |
1616// | | |____| | |
1617// |_|/______\|_|
1618// |_|________|_|
1619//
1620// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001621//
1622// For filled rrects that need to provide a distance vector we resuse the overstroke
1623// geometry but make the inner rect degenerate (either a point or a horizontal or
1624// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001625
jvanverth84839f62016-08-29 10:16:40 -07001626static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001627 // clang-format off
1628 // overstroke quads
1629 // we place this at the beginning so that we can skip these indices when rendering normally
1630 16, 17, 19, 16, 19, 18,
1631 19, 17, 23, 19, 23, 21,
1632 21, 23, 22, 21, 22, 20,
1633 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001634
Brian Salomon289e3d82016-12-14 15:52:56 -05001635 // corners
1636 0, 1, 5, 0, 5, 4,
1637 2, 3, 7, 2, 7, 6,
1638 8, 9, 13, 8, 13, 12,
1639 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001640
Brian Salomon289e3d82016-12-14 15:52:56 -05001641 // edges
1642 1, 2, 6, 1, 6, 5,
1643 4, 5, 9, 4, 9, 8,
1644 6, 7, 11, 6, 11, 10,
1645 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001646
Brian Salomon289e3d82016-12-14 15:52:56 -05001647 // center
1648 // we place this at the end so that we can ignore these indices when not rendering as filled
1649 5, 6, 10, 5, 10, 9,
1650 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001651};
Brian Salomon289e3d82016-12-14 15:52:56 -05001652
jvanverth84839f62016-08-29 10:16:40 -07001653// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001654static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001655
jvanverth84839f62016-08-29 10:16:40 -07001656// overstroke count is arraysize minus the center indices
1657static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1658// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001659static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001660// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001661static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1662static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001663static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001664
jvanverthc3d0e422016-08-25 08:12:35 -07001665enum RRectType {
1666 kFill_RRectType,
1667 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001668 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07001669};
1670
jvanverth84839f62016-08-29 10:16:40 -07001671static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001672 switch (type) {
1673 case kFill_RRectType:
1674 case kStroke_RRectType:
1675 return kVertsPerStandardRRect;
1676 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001677 return kVertsPerOverstrokeRRect;
1678 }
1679 SkFAIL("Invalid type");
1680 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001681}
1682
1683static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001684 switch (type) {
1685 case kFill_RRectType:
1686 return kIndicesPerFillRRect;
1687 case kStroke_RRectType:
1688 return kIndicesPerStrokeRRect;
1689 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001690 return kIndicesPerOverstrokeRRect;
1691 }
1692 SkFAIL("Invalid type");
1693 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001694}
1695
1696static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001697 switch (type) {
1698 case kFill_RRectType:
1699 case kStroke_RRectType:
1700 return gStandardRRectIndices;
1701 case kOverstroke_RRectType:
Brian Salomon289e3d82016-12-14 15:52:56 -05001702 return gOverstrokeRRectIndices;
1703 }
1704 SkFAIL("Invalid type");
1705 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001706}
1707
joshualitt76e7fb62015-02-11 08:52:27 -08001708///////////////////////////////////////////////////////////////////////////////////////////////////
1709
Robert Phillips79839d42016-10-06 15:03:34 -04001710// For distance computations in the interior of filled rrects we:
1711//
1712// add a interior degenerate (point or line) rect
1713// each vertex of that rect gets -outerRad as its radius
1714// this makes the computation of the distance to the outer edge be negative
1715// negative values are caught and then handled differently in the GP's onEmitCode
1716// each vertex is also given the normalized x & y distance from the interior rect's edge
1717// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1718
Brian Salomon05441c42017-05-15 16:45:49 -04001719class CircularRRectOp : public GrMeshDrawOp {
1720private:
1721 using Helper = GrSimpleMeshDrawOpHelper;
1722
joshualitt76e7fb62015-02-11 08:52:27 -08001723public:
Brian Salomon25a88092016-12-01 09:36:50 -05001724 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001725
bsalomon4b4a7cc2016-07-08 04:42:54 -07001726 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1727 // whether the rrect is only stroked or stroked and filled.
Brian Salomone23bffd2017-06-02 11:01:10 -04001728 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1729 const SkRect& devRect, float devRadius,
1730 float devStrokeWidth, bool strokeOnly) {
1731 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
1732 devRadius, devStrokeWidth, strokeOnly);
Brian Salomon05441c42017-05-15 16:45:49 -04001733 }
Brian Salomone23bffd2017-06-02 11:01:10 -04001734 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1735 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
Brian Salomon05441c42017-05-15 16:45:49 -04001736 : INHERITED(ClassID())
1737 , fViewMatrixIfUsingLocalCoords(viewMatrix)
1738 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001739 SkRect bounds = devRect;
1740 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1741 SkScalar innerRadius = 0.0f;
1742 SkScalar outerRadius = devRadius;
1743 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001744 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001745 if (devStrokeWidth > 0) {
1746 if (SkScalarNearlyZero(devStrokeWidth)) {
1747 halfWidth = SK_ScalarHalf;
1748 } else {
1749 halfWidth = SkScalarHalf(devStrokeWidth);
1750 }
joshualitt76e7fb62015-02-11 08:52:27 -08001751
bsalomon4b4a7cc2016-07-08 04:42:54 -07001752 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001753 // Outset stroke by 1/4 pixel
1754 devStrokeWidth += 0.25f;
1755 // If stroke is greater than width or height, this is still a fill
1756 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001757 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001758 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001759 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001760 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001761 }
1762 outerRadius += halfWidth;
1763 bounds.outset(halfWidth, halfWidth);
1764 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001765
bsalomon4b4a7cc2016-07-08 04:42:54 -07001766 // The radii are outset for two reasons. First, it allows the shader to simply perform
1767 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1768 // Second, the outer radius is used to compute the verts of the bounding box that is
1769 // rendered and the outset ensures the box will cover all partially covered by the rrect
1770 // corners.
1771 outerRadius += SK_ScalarHalf;
1772 innerRadius -= SK_ScalarHalf;
1773
bsalomon88cf17d2016-07-08 06:40:56 -07001774 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1775
1776 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001777 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1778
Brian Salomon05441c42017-05-15 16:45:49 -04001779 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001780 fVertCount = rrect_type_to_vert_count(type);
1781 fIndexCount = rrect_type_to_index_count(type);
1782 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001783 }
1784
Brian Salomon289e3d82016-12-14 15:52:56 -05001785 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001786
jvanverthc3d0e422016-08-25 08:12:35 -07001787 SkString dumpInfo() const override {
1788 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001789 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001790 string.appendf(
1791 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1792 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001793 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1794 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1795 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001796 }
1797 string.append(INHERITED::dumpInfo());
1798 return string;
1799 }
1800
Brian Salomon05441c42017-05-15 16:45:49 -04001801 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1802 GrColor* color = &fRRects.front().fColor;
1803 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1804 color);
1805 }
1806
1807 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1808
Brian Salomon92aee3d2016-12-21 09:20:25 -05001809private:
Robert Phillips79839d42016-10-06 15:03:34 -04001810 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001811 SkPoint fPos;
1812 GrColor fColor;
1813 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001814 SkScalar fOuterRadius;
1815 SkScalar fInnerRadius;
1816 // No half plane, we don't use it here.
1817 };
1818
Brian Salomon289e3d82016-12-14 15:52:56 -05001819 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1820 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1821 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001822 SkASSERT(smInset < bigInset);
1823
1824 // TL
1825 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1826 (*verts)->fColor = color;
1827 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1828 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001829 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001830 (*verts)++;
1831
1832 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001833 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001834 (*verts)->fColor = color;
1835 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1836 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001837 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001838 (*verts)++;
1839
Brian Salomon289e3d82016-12-14 15:52:56 -05001840 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001841 (*verts)->fColor = color;
1842 (*verts)->fOffset = SkPoint::Make(0, 0);
1843 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001844 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001845 (*verts)++;
1846
Brian Salomon289e3d82016-12-14 15:52:56 -05001847 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001848 (*verts)->fColor = color;
1849 (*verts)->fOffset = SkPoint::Make(0, 0);
1850 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001851 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001852 (*verts)++;
1853
1854 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1855 (*verts)->fColor = color;
1856 (*verts)->fOffset = SkPoint::Make(0, 0);
1857 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001858 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001859 (*verts)++;
1860
1861 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1862 (*verts)->fColor = color;
1863 (*verts)->fOffset = SkPoint::Make(0, 0);
1864 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001865 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001866 (*verts)++;
1867
1868 // BL
1869 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1870 (*verts)->fColor = color;
1871 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1872 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001873 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001874 (*verts)++;
1875
1876 // BR
1877 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1878 (*verts)->fColor = color;
1879 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1880 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001881 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001882 (*verts)++;
1883 }
1884
joshualitt144c3c82015-11-30 12:30:13 -08001885 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001886 // Invert the view matrix as a local matrix (if any other processors require coords).
1887 SkMatrix localMatrix;
1888 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001889 return;
1890 }
1891
1892 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001893 sk_sp<GrGeometryProcessor> gp(
1894 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001895
joshualitt76e7fb62015-02-11 08:52:27 -08001896 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001897 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001898
jvanverth84839f62016-08-29 10:16:40 -07001899 const GrBuffer* vertexBuffer;
1900 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001901
Brian Salomon289e3d82016-12-14 15:52:56 -05001902 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1903 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001904 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001905 SkDebugf("Could not allocate vertices\n");
1906 return;
1907 }
1908
jvanverth84839f62016-08-29 10:16:40 -07001909 const GrBuffer* indexBuffer = nullptr;
1910 int firstIndex = 0;
1911 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1912 if (!indices) {
1913 SkDebugf("Could not allocate indices\n");
1914 return;
1915 }
1916
1917 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001918 for (const auto& rrect : fRRects) {
1919 GrColor color = rrect.fColor;
1920 SkScalar outerRadius = rrect.fOuterRadius;
1921 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001922
Brian Salomon289e3d82016-12-14 15:52:56 -05001923 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1924 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001925
Brian Salomon289e3d82016-12-14 15:52:56 -05001926 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001927 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001928 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomone23bffd2017-06-02 11:01:10 -04001929 SkScalar innerRadius = rrect.fType != kFill_RRectType
1930 ? rrect.fInnerRadius / rrect.fOuterRadius
1931 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001932 for (int i = 0; i < 4; ++i) {
1933 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001934 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001935 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1936 verts->fOuterRadius = outerRadius;
1937 verts->fInnerRadius = innerRadius;
1938 verts++;
1939
1940 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001941 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001942 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1943 verts->fOuterRadius = outerRadius;
1944 verts->fInnerRadius = innerRadius;
1945 verts++;
1946
1947 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001948 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001949 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1950 verts->fOuterRadius = outerRadius;
1951 verts->fInnerRadius = innerRadius;
1952 verts++;
1953
1954 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001955 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001956 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1957 verts->fOuterRadius = outerRadius;
1958 verts->fInnerRadius = innerRadius;
1959 verts++;
1960 }
jvanverthc3d0e422016-08-25 08:12:35 -07001961 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001962 // Effectively this is an additional stroked rrect, with its
1963 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1964 // This will give us correct AA in the center and the correct
1965 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001966 //
jvanvertha4f1af82016-08-29 07:17:47 -07001967 // Also, the outer offset is a constant vector pointing to the right, which
1968 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04001969 if (kOverstroke_RRectType == rrect.fType) {
1970 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04001971
Brian Salomon05441c42017-05-15 16:45:49 -04001972 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001973 // this is the normalized distance from the outer rectangle of this
1974 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04001975 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001976
Brian Salomon289e3d82016-12-14 15:52:56 -05001977 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04001978 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04001979 }
jvanverth6a397612016-08-26 08:15:33 -07001980
Brian Salomon05441c42017-05-15 16:45:49 -04001981 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
1982 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07001983 for (int i = 0; i < primIndexCount; ++i) {
1984 *indices++ = primIndices[i] + currStartVertex;
1985 }
1986
Brian Salomon05441c42017-05-15 16:45:49 -04001987 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001988 }
1989
Chris Dalton3809bab2017-06-13 10:55:06 -06001990 GrMesh mesh(GrPrimitiveType::kTriangles);
Chris Dalton114a3c02017-05-26 15:17:19 -06001991 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1992 mesh.setVertexData(vertexBuffer, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001993 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001994 }
1995
Brian Salomon25a88092016-12-01 09:36:50 -05001996 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001997 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001998
1999 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002000 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002001 return false;
2002 }
2003
Brian Salomon05441c42017-05-15 16:45:49 -04002004 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002005 return false;
2006 }
2007
Brian Salomon05441c42017-05-15 16:45:49 -04002008 if (fHelper.usesLocalCoords() &&
2009 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002010 return false;
2011 }
2012
Brian Salomon05441c42017-05-15 16:45:49 -04002013 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002014 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002015 fVertCount += that->fVertCount;
2016 fIndexCount += that->fIndexCount;
2017 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002018 return true;
2019 }
2020
Brian Salomon05441c42017-05-15 16:45:49 -04002021 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002022 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002023 SkScalar fInnerRadius;
2024 SkScalar fOuterRadius;
2025 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002026 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002027 };
2028
Brian Salomon289e3d82016-12-14 15:52:56 -05002029 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002030 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002031 int fVertCount;
2032 int fIndexCount;
2033 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002034 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002035
Brian Salomon05441c42017-05-15 16:45:49 -04002036 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002037};
2038
jvanverth84839f62016-08-29 10:16:40 -07002039static const int kNumRRectsInIndexBuffer = 256;
2040
2041GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2042GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2043static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2044 GrResourceProvider* resourceProvider) {
2045 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2046 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2047 switch (type) {
2048 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002049 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002050 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2051 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002052 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002053 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002054 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2055 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002056 default:
2057 SkASSERT(false);
2058 return nullptr;
2059 };
2060}
2061
Brian Salomon05441c42017-05-15 16:45:49 -04002062class EllipticalRRectOp : public GrMeshDrawOp {
2063private:
2064 using Helper = GrSimpleMeshDrawOpHelper;
2065
joshualitt76e7fb62015-02-11 08:52:27 -08002066public:
Brian Salomon25a88092016-12-01 09:36:50 -05002067 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002068
bsalomon4b4a7cc2016-07-08 04:42:54 -07002069 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2070 // whether the rrect is only stroked or stroked and filled.
Brian Salomon05441c42017-05-15 16:45:49 -04002071 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2072 const SkRect& devRect, float devXRadius, float devYRadius,
2073 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002074 SkASSERT(devXRadius > 0.5);
2075 SkASSERT(devYRadius > 0.5);
2076 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2077 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002078 if (devStrokeWidths.fX > 0) {
2079 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2080 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2081 } else {
2082 devStrokeWidths.scale(SK_ScalarHalf);
2083 }
joshualitt76e7fb62015-02-11 08:52:27 -08002084
bsalomon4b4a7cc2016-07-08 04:42:54 -07002085 // we only handle thick strokes for near-circular ellipses
2086 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002087 (SK_ScalarHalf * devXRadius > devYRadius ||
2088 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002089 return nullptr;
2090 }
2091
2092 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002093 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2094 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002095 return nullptr;
2096 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002097 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2098 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002099 return nullptr;
2100 }
Brian Salomon05441c42017-05-15 16:45:49 -04002101 }
2102 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2103 devXRadius, devYRadius, devStrokeWidths,
2104 strokeOnly);
2105 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002106
Brian Salomon05441c42017-05-15 16:45:49 -04002107 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2108 const SkRect& devRect, float devXRadius, float devYRadius,
2109 SkVector devStrokeHalfWidths, bool strokeOnly)
2110 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2111 SkScalar innerXRadius = 0.0f;
2112 SkScalar innerYRadius = 0.0f;
2113 SkRect bounds = devRect;
2114 bool stroked = false;
2115 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002116 // this is legit only if scale & translation (which should be the case at the moment)
2117 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002118 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2119 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002120 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2121 }
2122
Brian Salomon05441c42017-05-15 16:45:49 -04002123 devXRadius += devStrokeHalfWidths.fX;
2124 devYRadius += devStrokeHalfWidths.fY;
2125 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002126 }
2127
Brian Salomon05441c42017-05-15 16:45:49 -04002128 fStroked = stroked;
2129 fViewMatrixIfUsingLocalCoords = viewMatrix;
2130 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002131 // Expand the rect for aa in order to generate the correct vertices.
2132 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002133 fRRects.emplace_back(
2134 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002135 }
2136
Brian Salomon289e3d82016-12-14 15:52:56 -05002137 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002138
Brian Salomon7c3e7182016-12-01 09:35:30 -05002139 SkString dumpInfo() const override {
2140 SkString string;
2141 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002142 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002143 string.appendf(
2144 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2145 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2146 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2147 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2148 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002149 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05002150 string.append(INHERITED::dumpInfo());
2151 return string;
2152 }
2153
Brian Salomon05441c42017-05-15 16:45:49 -04002154 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
2155 GrColor* color = &fRRects.front().fColor;
2156 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2157 color);
2158 }
2159
2160 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2161
bsalomone46f9fe2015-08-18 06:05:14 -07002162private:
joshualitt144c3c82015-11-30 12:30:13 -08002163 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002164 SkMatrix localMatrix;
2165 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002166 return;
2167 }
2168
2169 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002170 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002171
joshualitt76e7fb62015-02-11 08:52:27 -08002172 size_t vertexStride = gp->getVertexStride();
2173 SkASSERT(vertexStride == sizeof(EllipseVertex));
2174
bsalomonb5238a72015-05-05 07:49:49 -07002175 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002176 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002177 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2178 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002179
Chris Dalton3809bab2017-06-13 10:55:06 -06002180 PatternHelper helper(GrPrimitiveType::kTriangles);
bsalomonb5238a72015-05-05 07:49:49 -07002181 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002182 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002183 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002184 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002185 SkDebugf("Could not allocate vertices\n");
2186 return;
2187 }
2188
Brian Salomon05441c42017-05-15 16:45:49 -04002189 for (const auto& rrect : fRRects) {
2190 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002191 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002192 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2193 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2194 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2195 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002196
2197 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002198 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2199 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002200
Brian Salomon05441c42017-05-15 16:45:49 -04002201 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002202
Brian Salomon289e3d82016-12-14 15:52:56 -05002203 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2204 bounds.fBottom - yOuterRadius, bounds.fBottom};
2205 SkScalar yOuterOffsets[4] = {yOuterRadius,
2206 SK_ScalarNearlyZero, // we're using inversesqrt() in
2207 // shader, so can't be exactly 0
2208 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002209
2210 for (int i = 0; i < 4; ++i) {
2211 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002212 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002213 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2214 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2215 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2216 verts++;
2217
2218 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002219 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002220 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2221 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2222 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2223 verts++;
2224
2225 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002226 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002227 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2228 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2229 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2230 verts++;
2231
2232 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002233 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002234 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2235 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2236 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2237 verts++;
2238 }
2239 }
Brian Salomon05441c42017-05-15 16:45:49 -04002240 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002241 }
2242
Brian Salomon25a88092016-12-01 09:36:50 -05002243 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002244 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002245
Brian Salomon05441c42017-05-15 16:45:49 -04002246 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002247 return false;
2248 }
2249
bsalomoncdaa97b2016-03-08 08:30:14 -08002250 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002251 return false;
2252 }
2253
Brian Salomon05441c42017-05-15 16:45:49 -04002254 if (fHelper.usesLocalCoords() &&
2255 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002256 return false;
2257 }
2258
Brian Salomon05441c42017-05-15 16:45:49 -04002259 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002260 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002261 return true;
2262 }
2263
Brian Salomon05441c42017-05-15 16:45:49 -04002264 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002265 GrColor fColor;
2266 SkScalar fXRadius;
2267 SkScalar fYRadius;
2268 SkScalar fInnerXRadius;
2269 SkScalar fInnerYRadius;
2270 SkRect fDevBounds;
2271 };
2272
Brian Salomon289e3d82016-12-14 15:52:56 -05002273 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002274 Helper fHelper;
2275 bool fStroked;
2276 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002277
Brian Salomon05441c42017-05-15 16:45:49 -04002278 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002279};
2280
Brian Salomon05441c42017-05-15 16:45:49 -04002281static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002282 const SkMatrix& viewMatrix,
2283 const SkRRect& rrect,
2284 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002285 SkASSERT(viewMatrix.rectStaysRect());
2286 SkASSERT(rrect.isSimple());
2287 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002288
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002289 // RRect ops only handle simple, but not too simple, rrects.
2290 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002291 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002292 SkRect bounds;
2293 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002294
2295 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002296 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2297 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2298 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2299 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002300
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002301 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002302
bsalomon4b4a7cc2016-07-08 04:42:54 -07002303 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2304 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002306
Brian Salomon289e3d82016-12-14 15:52:56 -05002307 bool isStrokeOnly =
2308 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002309 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2310
jvanverthc3d0e422016-08-25 08:12:35 -07002311 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002312 if (hasStroke) {
2313 if (SkStrokeRec::kHairline_Style == style) {
2314 scaledStroke.set(1, 1);
2315 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002316 scaledStroke.fX = SkScalarAbs(
2317 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2318 scaledStroke.fY = SkScalarAbs(
2319 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002320 }
2321
jvanverthc3d0e422016-08-25 08:12:35 -07002322 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2323 // for non-circular rrects, if half of strokewidth is greater than radius,
2324 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002325 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2326 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002327 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002328 }
2329 }
2330
2331 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2332 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2333 // patch will have fractional coverage. This only matters when the interior is actually filled.
2334 // We could consider falling back to rect rendering here, since a tiny radius is
2335 // indistinguishable from a square corner.
2336 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002337 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002338 }
2339
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002340 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002341 if (isCircular) {
Brian Salomone23bffd2017-06-02 11:01:10 -04002342 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
2343 isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002344 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002345 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04002346 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2347 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002348 }
joshualitt3e708c52015-04-30 13:49:27 -07002349}
2350
Brian Salomon05441c42017-05-15 16:45:49 -04002351std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
Brian Salomon05441c42017-05-15 16:45:49 -04002352 const SkMatrix& viewMatrix,
2353 const SkRRect& rrect,
2354 const SkStrokeRec& stroke,
2355 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002356 if (rrect.isOval()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002357 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002358 }
2359
2360 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002361 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002362 }
2363
Brian Salomone23bffd2017-06-02 11:01:10 -04002364 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002365}
joshualitt3e708c52015-04-30 13:49:27 -07002366
bsalomon4b4a7cc2016-07-08 04:42:54 -07002367///////////////////////////////////////////////////////////////////////////////
2368
Brian Salomon05441c42017-05-15 16:45:49 -04002369std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2370 const SkMatrix& viewMatrix,
2371 const SkRect& oval,
2372 const SkStrokeRec& stroke,
2373 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002374 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002375 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002376 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2377 circle_stays_circle(viewMatrix)) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002378 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon05441c42017-05-15 16:45:49 -04002379 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2380 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002381 }
2382
Stan Ilieveb868aa2017-02-21 11:06:16 -05002383 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002384 if (viewMatrix.rectStaysRect()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002385 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002386 }
2387
Stan Ilieveb868aa2017-02-21 11:06:16 -05002388 // Otherwise, if we have shader derivative support, render as device-independent
2389 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002390 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
Stan Ilieveb868aa2017-02-21 11:06:16 -05002391 }
2392
bsalomon4b4a7cc2016-07-08 04:42:54 -07002393 return nullptr;
2394}
2395
2396///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002397
Brian Salomon05441c42017-05-15 16:45:49 -04002398std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2399 const SkRect& oval, SkScalar startAngle,
2400 SkScalar sweepAngle, bool useCenter,
2401 const GrStyle& style,
2402 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002403 SkASSERT(!oval.isEmpty());
2404 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002405 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002406 if (SkScalarAbs(sweepAngle) >= 360.f) {
2407 return nullptr;
2408 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002409 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2410 return nullptr;
2411 }
2412 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002413 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2414 useCenter};
Brian Salomon05441c42017-05-15 16:45:49 -04002415 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002416}
2417
2418///////////////////////////////////////////////////////////////////////////////
2419
Hal Canary6f6961e2017-01-31 13:50:44 -05002420#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002421
Brian Salomon05441c42017-05-15 16:45:49 -04002422GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002423 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002424 SkScalar rotate = random->nextSScalar1() * 360.f;
2425 SkScalar translateX = random->nextSScalar1() * 1000.f;
2426 SkScalar translateY = random->nextSScalar1() * 1000.f;
2427 SkScalar scale = random->nextSScalar1() * 100.f;
2428 SkMatrix viewMatrix;
2429 viewMatrix.setRotate(rotate);
2430 viewMatrix.postTranslate(translateX, translateY);
2431 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002432 SkRect circle = GrTest::TestSquare(random);
2433 SkPoint center = {circle.centerX(), circle.centerY()};
2434 SkScalar radius = circle.width() / 2.f;
2435 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002436 CircleOp::ArcParams arcParamsTmp;
2437 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002438 if (random->nextBool()) {
2439 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002440 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2441 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002442 arcParams = &arcParamsTmp;
2443 }
Brian Salomon05441c42017-05-15 16:45:49 -04002444 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2445 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002446 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002447 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002448 }
2449 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002450}
2451
Brian Salomon05441c42017-05-15 16:45:49 -04002452GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002453 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07002454 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002455 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002456}
2457
Brian Salomon05441c42017-05-15 16:45:49 -04002458GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002459 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07002460 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002461 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002462}
2463
Brian Salomon05441c42017-05-15 16:45:49 -04002464GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002465 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002466 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Brian Salomone23bffd2017-06-02 11:01:10 -04002467 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002468}
2469
2470#endif