blob: 5a9a0771010a7115b3d1e7f47a5086276c20670a [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"
commit-bot@chromium.org81312832013-03-22 18:34:09 +00009
Brian Salomon5ec9def2016-12-20 15:34:05 -050010#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080011#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080012#include "GrInvariantOutput.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050013#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050016#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070017#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000018#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000019#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080023#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080024#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050025#include "glsl/GrGLSLVarying.h"
26#include "glsl/GrGLSLVertexShaderBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050027#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000028
Brian Salomon25a88092016-12-01 09:36:50 -050029// TODO(joshualitt) - Break this file up during GrOp post implementation cleanup
joshualitt76e7fb62015-02-11 08:52:27 -080030
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080032
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050034 SkPoint fPos;
35 GrColor fColor;
36 SkPoint fOffset;
37 SkPoint fOuterRadii;
38 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000039};
40
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000041struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050042 SkPoint fPos;
43 GrColor fColor;
44 SkPoint fOuterOffset;
45 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046};
47
Brian Salomon289e3d82016-12-14 15:52:56 -050048static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000049}
50
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000051///////////////////////////////////////////////////////////////////////////////
52
53/**
bsalomonce1c8862014-12-15 07:11:22 -080054 * The output of this effect is a modulation of the input color and coverage for a circle. It
55 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080056 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080057 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec4f : (p.xy, outerRad, innerRad)
60 * p is the position in the normalized space.
61 * outerRad is the outerRadius in device space.
62 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040063 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070064 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
65 * vec4f : (v.xy, outerDistance, innerDistance)
66 * v is a normalized vector pointing to the outer edge
67 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
68 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070069 * Additional clip planes are supported for rendering circular arcs. The additional planes are
70 * either intersected or unioned together. Up to three planes are supported (an initial plane,
71 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050072 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070073 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074 */
75
bsalomoncdaa97b2016-03-08 08:30:14 -080076class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
79 const SkMatrix& localMatrix)
80 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080081 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070082 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
83 kHigh_GrSLPrecision);
84 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
Jim Van Verth6750e912016-12-19 14:45:19 -050085 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
86 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -070087 if (clipPlane) {
88 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
89 } else {
90 fInClipPlane = nullptr;
91 }
92 if (isectPlane) {
93 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
94 } else {
95 fInIsectPlane = nullptr;
96 }
97 if (unionPlane) {
98 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
99 } else {
100 fInUnionPlane = nullptr;
101 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800102 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000103 }
104
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400105 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700106
bsalomoncdaa97b2016-03-08 08:30:14 -0800107 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000108
mtklein36352bf2015-03-25 18:17:31 -0700109 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110
Brian Salomon94efbf52016-11-29 13:43:05 -0500111 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700112 GLSLProcessor::GenKey(*this, caps, b);
113 }
114
Brian Salomon94efbf52016-11-29 13:43:05 -0500115 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700116 return new GLSLProcessor();
117 }
118
119private:
egdaniel57d3b032015-11-13 11:57:27 -0800120 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000121 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800122 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000123
Brian Salomon289e3d82016-12-14 15:52:56 -0500124 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800125 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800126 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800127 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800128 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700129 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800130
joshualittabb52a12015-01-13 15:02:10 -0800131 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800132 varyingHandler->emitAttributes(cgp);
Jim Van Verth6750e912016-12-19 14:45:19 -0500133 fragBuilder->codeAppend("highp vec4 circleEdge;");
134 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
135 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700164 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800165
Jim Van Verth6750e912016-12-19 14:45:19 -0500166 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700167 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
168 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800169 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500170 fragBuilder->codeAppend(
171 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700172 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800173 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000175
dvonbeck68f2f7d2016-08-01 11:37:45 -0700176 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700177 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
Brian Salomon289e3d82016-12-14 15:52:56 -0500178 fragBuilder->codeAppendf(
179 "if (d == 0.0) {" // if on the center of the circle
180 " %s = vec4(1.0, 0.0, distanceToOuterEdge, "
181 " %s);", // no normalize
182 args.fDistanceVectorName,
183 innerEdgeDistance);
184 fragBuilder->codeAppendf(
185 "} else {"
186 " %s = vec4(normalize(circleEdge.xy),"
187 " distanceToOuterEdge, %s);"
188 "}",
189 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700190 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700191 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500192 fragBuilder->codeAppend(
193 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
194 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700195 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500196 fragBuilder->codeAppend(
197 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
198 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700199 }
200 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500201 fragBuilder->codeAppend(
202 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
203 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700204 }
205 fragBuilder->codeAppend("edgeAlpha *= clip;");
206 }
egdaniel4ca2e602015-11-18 08:01:26 -0800207 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208 }
209
robertphillips46d36f02015-01-18 08:14:14 -0800210 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500211 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700212 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800213 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700214 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500215 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700216 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500217 key |= cgp.fInClipPlane ? 0x04 : 0x0;
218 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
219 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700220 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 }
222
bsalomona624bf32016-09-20 09:12:47 -0700223 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
224 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700225 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700226 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700227 }
228
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229 private:
egdaniele659a582015-11-13 09:55:43 -0800230 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 };
232
Brian Salomon289e3d82016-12-14 15:52:56 -0500233 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800234 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800235 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800236 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700237 const Attribute* fInClipPlane;
238 const Attribute* fInIsectPlane;
239 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500240 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241
joshualittb0a8a372014-09-23 09:50:21 -0700242 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243
joshualitt249af152014-09-15 11:41:13 -0700244 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245};
246
bsalomoncdaa97b2016-03-08 08:30:14 -0800247GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248
bungeman06ca8ec2016-06-09 08:01:03 -0700249sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500250 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
251 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
252 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253}
254
255///////////////////////////////////////////////////////////////////////////////
256
257/**
258 * 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 +0000259 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
260 * in both x and y directions.
261 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000262 * 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 +0000263 */
264
bsalomoncdaa97b2016-03-08 08:30:14 -0800265class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500267 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800268 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700269 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
270 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
271 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
272 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800273 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000274 }
275
bsalomoncdaa97b2016-03-08 08:30:14 -0800276 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000277
mtklein36352bf2015-03-25 18:17:31 -0700278 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800279
Brian Salomon94efbf52016-11-29 13:43:05 -0500280 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700281 GLSLProcessor::GenKey(*this, caps, b);
282 }
283
Brian Salomon94efbf52016-11-29 13:43:05 -0500284 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700285 return new GLSLProcessor();
286 }
287
288private:
egdaniel57d3b032015-11-13 11:57:27 -0800289 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800291 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000292
Brian Salomon289e3d82016-12-14 15:52:56 -0500293 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800294 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800295 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800296 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800297 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000298
joshualittabb52a12015-01-13 15:02:10 -0800299 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800300 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800301
egdaniel8dcdedc2015-11-11 06:27:20 -0800302 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800303 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800304 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700305 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000306
egdaniel8dcdedc2015-11-11 06:27:20 -0800307 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800308 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500309 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800310
cdalton85285412016-02-18 12:37:07 -0800311 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700312 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700313 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800314
joshualittabb52a12015-01-13 15:02:10 -0800315 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700316 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800317
318 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800319 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800320 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800321 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800322 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700323 egp.fInPosition->fName,
324 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700325 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800326
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000327 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800328 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
329 ellipseRadii.fsIn());
330 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
331 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
332 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700333
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000334 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800335 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
336 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800337 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000338
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000339 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800340 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500341 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800342 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500343 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
344 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800345 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
346 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000347 }
348
egdaniel4ca2e602015-11-18 08:01:26 -0800349 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000350 }
351
robertphillips46d36f02015-01-18 08:14:14 -0800352 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500353 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700354 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800355 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
356 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700357 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700358 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000359 }
360
bsalomona624bf32016-09-20 09:12:47 -0700361 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
362 FPCoordTransformIter&& transformIter) override {
363 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
364 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700365 }
366
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367 private:
egdaniele659a582015-11-13 09:55:43 -0800368 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369 };
370
joshualitt71c92602015-01-14 08:12:47 -0800371 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800372 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800373 const Attribute* fInEllipseOffset;
374 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700375 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000376 bool fStroke;
377
joshualittb0a8a372014-09-23 09:50:21 -0700378 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000379
joshualitt249af152014-09-15 11:41:13 -0700380 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000381};
382
bsalomoncdaa97b2016-03-08 08:30:14 -0800383GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384
bungeman06ca8ec2016-06-09 08:01:03 -0700385sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
386 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500387 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000388}
389
390///////////////////////////////////////////////////////////////////////////////
391
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000392/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000393 * 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 +0000394 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
395 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
396 * using differentials.
397 *
398 * The result is device-independent and can be used with any affine matrix.
399 */
400
bsalomoncdaa97b2016-03-08 08:30:14 -0800401enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000402
bsalomoncdaa97b2016-03-08 08:30:14 -0800403class DIEllipseGeometryProcessor : public GrGeometryProcessor {
404public:
405 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500406 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800407 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700408 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
409 kHigh_GrSLPrecision);
410 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
411 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
412 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800413 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000414 }
415
bsalomoncdaa97b2016-03-08 08:30:14 -0800416 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000417
mtklein36352bf2015-03-25 18:17:31 -0700418 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000419
Brian Salomon94efbf52016-11-29 13:43:05 -0500420 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700421 GLSLProcessor::GenKey(*this, caps, b);
422 }
halcanary9d524f22016-03-29 09:03:52 -0700423
Brian Salomon94efbf52016-11-29 13:43:05 -0500424 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700425 return new GLSLProcessor();
426 }
427
428private:
egdaniel57d3b032015-11-13 11:57:27 -0800429 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000430 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500431 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000432
joshualitt465283c2015-09-11 08:19:35 -0700433 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800434 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800435 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800436 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800437 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000438
joshualittabb52a12015-01-13 15:02:10 -0800439 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800440 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800441
egdaniel8dcdedc2015-11-11 06:27:20 -0800442 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800443 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500444 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700445
egdaniel8dcdedc2015-11-11 06:27:20 -0800446 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800447 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500448 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800449
cdalton85285412016-02-18 12:37:07 -0800450 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700451 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800452
joshualittabb52a12015-01-13 15:02:10 -0800453 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800454 this->setupPosition(vertBuilder,
455 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800456 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700457 diegp.fInPosition->fName,
458 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700459 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800460
461 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800462 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800463 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800464 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800465 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700466 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700467 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800468
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000469 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800470 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
471 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
472 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
473 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500474 fragBuilder->codeAppendf(
475 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
476 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
477 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000478
egdaniel4ca2e602015-11-18 08:01:26 -0800479 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000480 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800481 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
482 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800483 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000484 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800485 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
486 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800488 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000489 }
490
491 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800492 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800493 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
494 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
495 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
496 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500497 fragBuilder->codeAppendf(
498 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
499 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
500 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800501 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
502 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000503 }
504
egdaniel4ca2e602015-11-18 08:01:26 -0800505 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506 }
507
robertphillips46d36f02015-01-18 08:14:14 -0800508 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500509 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700510 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800511 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
512 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700513 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700514 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000515 }
516
bsalomona624bf32016-09-20 09:12:47 -0700517 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
518 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800519 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700520
bsalomon31df31c2016-08-17 09:00:24 -0700521 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
522 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700523 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800524 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700525 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
526 }
bsalomona624bf32016-09-20 09:12:47 -0700527 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000528 }
529
530 private:
joshualitt5559ca22015-05-21 15:50:36 -0700531 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700532 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800533
egdaniele659a582015-11-13 09:55:43 -0800534 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000535 };
536
joshualitt71c92602015-01-14 08:12:47 -0800537 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800538 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800539 const Attribute* fInEllipseOffsets0;
540 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500541 SkMatrix fViewMatrix;
542 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000543
joshualittb0a8a372014-09-23 09:50:21 -0700544 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000545
joshualitt249af152014-09-15 11:41:13 -0700546 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000547};
548
bsalomoncdaa97b2016-03-08 08:30:14 -0800549GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000550
bungeman06ca8ec2016-06-09 08:01:03 -0700551sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500552 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
553 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000554}
555
556///////////////////////////////////////////////////////////////////////////////
557
jvanverth6ca48822016-10-07 06:57:32 -0700558// We have two possible cases for geometry for a circle:
559
560// In the case of a normal fill, we draw geometry for the circle as an octagon.
561static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500562 // enter the octagon
563 // clang-format off
564 0, 1, 8, 1, 2, 8,
565 2, 3, 8, 3, 4, 8,
566 4, 5, 8, 5, 6, 8,
567 6, 7, 8, 7, 0, 8
568 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700569};
570
571// For stroked circles, we use two nested octagons.
572static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500573 // enter the octagon
574 // clang-format off
575 0, 1, 9, 0, 9, 8,
576 1, 2, 10, 1, 10, 9,
577 2, 3, 11, 2, 11, 10,
578 3, 4, 12, 3, 12, 11,
579 4, 5, 13, 4, 13, 12,
580 5, 6, 14, 5, 14, 13,
581 6, 7, 15, 6, 15, 14,
582 7, 0, 8, 7, 8, 15,
583 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700584};
585
Brian Salomon289e3d82016-12-14 15:52:56 -0500586
jvanverth6ca48822016-10-07 06:57:32 -0700587static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
588static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
589static const int kVertsPerStrokeCircle = 16;
590static const int kVertsPerFillCircle = 9;
591
592static int circle_type_to_vert_count(bool stroked) {
593 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
594}
595
596static int circle_type_to_index_count(bool stroked) {
597 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
598}
599
600static const uint16_t* circle_type_to_indices(bool stroked) {
601 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
602}
603
604///////////////////////////////////////////////////////////////////////////////
605
Brian Salomon289e3d82016-12-14 15:52:56 -0500606class CircleOp final : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -0800607public:
Brian Salomon25a88092016-12-01 09:36:50 -0500608 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700609
bsalomon4f3a0ca2016-08-22 13:14:26 -0700610 /** Optional extra params to render a partial arc rather than a full circle. */
611 struct ArcParams {
612 SkScalar fStartAngleRadians;
613 SkScalar fSweepAngleRadians;
614 bool fUseCenter;
615 };
Brian Salomon289e3d82016-12-14 15:52:56 -0500616 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
617 SkScalar radius, const GrStyle& style,
618 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700619 SkASSERT(circle_stays_circle(viewMatrix));
620 const SkStrokeRec& stroke = style.strokeRec();
621 if (style.hasPathEffect()) {
622 return nullptr;
623 }
624 SkStrokeRec::Style recStyle = stroke.getStyle();
625 if (arcParams) {
626 // Arc support depends on the style.
627 switch (recStyle) {
628 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500629 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700630 return nullptr;
631 case SkStrokeRec::kFill_Style:
632 // This supports all fills.
633 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500634 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700635 case SkStrokeRec::kHairline_Style:
636 // Strokes that don't use the center point are supported with butt cap.
637 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
638 return nullptr;
639 }
640 break;
641 }
642 }
643
bsalomon4b4a7cc2016-07-08 04:42:54 -0700644 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700645 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700646 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800647
Brian Salomon289e3d82016-12-14 15:52:56 -0500648 bool isStrokeOnly =
649 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700650 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700651
jvanverth6ca48822016-10-07 06:57:32 -0700652 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700653 SkScalar outerRadius = radius;
654 SkScalar halfWidth = 0;
655 if (hasStroke) {
656 if (SkScalarNearlyZero(strokeWidth)) {
657 halfWidth = SK_ScalarHalf;
658 } else {
659 halfWidth = SkScalarHalf(strokeWidth);
660 }
661
662 outerRadius += halfWidth;
663 if (isStrokeOnly) {
664 innerRadius = radius - halfWidth;
665 }
666 }
667
668 // The radii are outset for two reasons. First, it allows the shader to simply perform
669 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
670 // Second, the outer radius is used to compute the verts of the bounding box that is
671 // rendered and the outset ensures the box will cover all partially covered by the circle.
672 outerRadius += SK_ScalarHalf;
673 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700674 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon289e3d82016-12-14 15:52:56 -0500675 sk_sp<CircleOp> op(new CircleOp());
676 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700677
bsalomon4f3a0ca2016-08-22 13:14:26 -0700678 // This makes every point fully inside the intersection plane.
679 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
680 // This makes every point fully outside the union plane.
681 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
682 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
683 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700684 if (arcParams) {
685 // The shader operates in a space where the circle is translated to be centered at the
686 // origin. Here we compute points on the unit circle at the starting and ending angles.
687 SkPoint startPoint, stopPoint;
688 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
689 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
690 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
691 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
692 // radial lines. However, in both cases we have to be careful about the half-circle.
693 // case. In that case the two radial lines are equal and so that edge gets clipped
694 // twice. Since the shared edge goes through the center we fall back on the useCenter
695 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500696 bool useCenter =
697 (arcParams->fUseCenter || isStrokeOnly) &&
698 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700699 if (useCenter) {
700 SkVector norm0 = {startPoint.fY, -startPoint.fX};
701 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
702 if (arcParams->fSweepAngleRadians > 0) {
703 norm0.negate();
704 } else {
705 norm1.negate();
706 }
Brian Salomon289e3d82016-12-14 15:52:56 -0500707 op->fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700708 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500709 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700710 color,
711 innerRadius,
712 outerRadius,
713 {norm0.fX, norm0.fY, 0.5f},
714 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
715 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700716 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500717 stroked});
718 op->fClipPlaneIsect = false;
719 op->fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700720 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500721 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700722 color,
723 innerRadius,
724 outerRadius,
725 {norm0.fX, norm0.fY, 0.5f},
726 {norm1.fX, norm1.fY, 0.5f},
727 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700728 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500729 stroked});
730 op->fClipPlaneIsect = true;
731 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700732 }
733 } else {
734 // We clip to a secant of the original circle.
735 startPoint.scale(radius);
736 stopPoint.scale(radius);
737 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
738 norm.normalize();
739 if (arcParams->fSweepAngleRadians > 0) {
740 norm.negate();
741 }
742 SkScalar d = -norm.dot(startPoint) + 0.5f;
743
Brian Salomon289e3d82016-12-14 15:52:56 -0500744 op->fGeoData.emplace_back(
745 Geometry{color,
746 innerRadius,
747 outerRadius,
748 {norm.fX, norm.fY, d},
749 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
750 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
751 devBounds,
752 stroked});
753 op->fClipPlane = true;
754 op->fClipPlaneIsect = false;
755 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700756 }
757 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500758 op->fGeoData.emplace_back(
759 Geometry{color,
760 innerRadius,
761 outerRadius,
762 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
763 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
764 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
765 devBounds,
766 stroked});
767 op->fClipPlane = false;
768 op->fClipPlaneIsect = false;
769 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700770 }
bsalomon88cf17d2016-07-08 06:40:56 -0700771 // Use the original radius and stroke radius for the bounds so that it does not include the
772 // AA bloat.
773 radius += halfWidth;
Brian Salomon289e3d82016-12-14 15:52:56 -0500774 op->setBounds(
775 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
776 HasAABloat::kYes, IsZeroArea::kNo);
777 op->fVertCount = circle_type_to_vert_count(stroked);
778 op->fIndexCount = circle_type_to_index_count(stroked);
779 op->fAllFill = !stroked;
780 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -0800781 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700782
Brian Salomon289e3d82016-12-14 15:52:56 -0500783 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800784
robertphillipse004bfc2015-11-16 09:06:59 -0800785 SkString dumpInfo() const override {
786 SkString string;
787 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500788 string.appendf(
789 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
790 "InnerRad: %.2f, OuterRad: %.2f\n",
791 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
792 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
793 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800794 }
Brian Salomon7c3e7182016-12-01 09:35:30 -0500795 string.append(DumpPipelineInfo(*this->pipeline()));
robertphillipse004bfc2015-11-16 09:06:59 -0800796 string.append(INHERITED::dumpInfo());
797 return string;
798 }
799
bsalomone46f9fe2015-08-18 06:05:14 -0700800private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 CircleOp() : INHERITED(ClassID()) {}
Brian Salomon92aee3d2016-12-21 09:20:25 -0500802
803 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
804 input->pipelineColorInput()->setKnownFourComponents(fGeoData[0].fColor);
805 input->pipelineCoverageInput()->setUnknownSingleComponent();
806 }
807
808 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
809 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
810 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800811 fViewMatrixIfUsingLocalCoords.reset();
812 }
joshualitt76e7fb62015-02-11 08:52:27 -0800813 }
814
joshualitt144c3c82015-11-30 12:30:13 -0800815 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800816 SkMatrix localMatrix;
817 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800818 return;
819 }
820
821 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500822 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
823 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700824
825 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500826 SkPoint fPos;
827 GrColor fColor;
828 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700829 SkScalar fOuterRadius;
830 SkScalar fInnerRadius;
831 // These planes may or may not be present in the vertex buffer.
832 SkScalar fHalfPlanes[3][3];
833 };
joshualitt76e7fb62015-02-11 08:52:27 -0800834
joshualitt76e7fb62015-02-11 08:52:27 -0800835 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800836 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500837 SkASSERT(vertexStride ==
838 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
839 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
840 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700841
842 const GrBuffer* vertexBuffer;
843 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500844 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
845 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700846 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700847 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800848 return;
849 }
850
jvanverth6ca48822016-10-07 06:57:32 -0700851 const GrBuffer* indexBuffer = nullptr;
852 int firstIndex = 0;
853 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
854 if (!indices) {
855 SkDebugf("Could not allocate indices\n");
856 return;
857 }
858
859 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800860 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800861 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800862
brianosmanbb2ff942016-02-11 14:15:18 -0800863 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700864 SkScalar innerRadius = geom.fInnerRadius;
865 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800866
bsalomonb5238a72015-05-05 07:49:49 -0700867 const SkRect& bounds = geom.fDevBounds;
Brian Salomon289e3d82016-12-14 15:52:56 -0500868 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
869 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
870 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
871 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
872 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
873 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
874 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
875 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800876
877 // The inner radius in the vertex data must be specified in normalized space.
878 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700879
880 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500881 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700882 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500883
Brian Salomon289e3d82016-12-14 15:52:56 -0500884 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700885 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700886 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700887 v0->fOuterRadius = outerRadius;
888 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800889
Brian Salomon289e3d82016-12-14 15:52:56 -0500890 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700891 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700892 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700893 v1->fOuterRadius = outerRadius;
894 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800895
Brian Salomon289e3d82016-12-14 15:52:56 -0500896 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700897 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700898 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700899 v2->fOuterRadius = outerRadius;
900 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800901
Brian Salomon289e3d82016-12-14 15:52:56 -0500902 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700903 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700904 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700905 v3->fOuterRadius = outerRadius;
906 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800907
Brian Salomon289e3d82016-12-14 15:52:56 -0500908 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700909 v4->fColor = color;
910 v4->fOffset = SkPoint::Make(octOffset, 1);
911 v4->fOuterRadius = outerRadius;
912 v4->fInnerRadius = innerRadius;
913
Brian Salomon289e3d82016-12-14 15:52:56 -0500914 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700915 v5->fColor = color;
916 v5->fOffset = SkPoint::Make(-octOffset, 1);
917 v5->fOuterRadius = outerRadius;
918 v5->fInnerRadius = innerRadius;
919
Brian Salomon289e3d82016-12-14 15:52:56 -0500920 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700921 v6->fColor = color;
922 v6->fOffset = SkPoint::Make(-1, octOffset);
923 v6->fOuterRadius = outerRadius;
924 v6->fInnerRadius = innerRadius;
925
Brian Salomon289e3d82016-12-14 15:52:56 -0500926 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700927 v7->fColor = color;
928 v7->fOffset = SkPoint::Make(-1, -octOffset);
929 v7->fOuterRadius = outerRadius;
930 v7->fInnerRadius = innerRadius;
931
bsalomon4f3a0ca2016-08-22 13:14:26 -0700932 if (fClipPlane) {
933 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
934 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
935 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
936 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700937 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
938 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
939 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700941 }
942 int unionIdx = 1;
943 if (fClipPlaneIsect) {
944 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
945 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
946 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
947 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700948 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
949 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
950 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700952 unionIdx = 2;
953 }
954 if (fClipPlaneUnion) {
955 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
956 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
957 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
958 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700959 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
960 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
961 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700963 }
jvanverth6ca48822016-10-07 06:57:32 -0700964
965 if (geom.fStroked) {
966 // compute the inner ring
967 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
968 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
969 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
970 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
971 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
972 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
973 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
974 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
975
976 // cosine and sine of pi/8
977 SkScalar c = 0.923579533f;
978 SkScalar s = 0.382683432f;
979 SkScalar r = geom.fInnerRadius;
980
Brian Salomon289e3d82016-12-14 15:52:56 -0500981 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700982 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500983 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700984 v0->fOuterRadius = outerRadius;
985 v0->fInnerRadius = innerRadius;
986
Brian Salomon289e3d82016-12-14 15:52:56 -0500987 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700988 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500989 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700990 v1->fOuterRadius = outerRadius;
991 v1->fInnerRadius = innerRadius;
992
Brian Salomon289e3d82016-12-14 15:52:56 -0500993 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700994 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500995 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700996 v2->fOuterRadius = outerRadius;
997 v2->fInnerRadius = innerRadius;
998
Brian Salomon289e3d82016-12-14 15:52:56 -0500999 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001000 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001001 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001002 v3->fOuterRadius = outerRadius;
1003 v3->fInnerRadius = innerRadius;
1004
Brian Salomon289e3d82016-12-14 15:52:56 -05001005 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001006 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001007 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001008 v4->fOuterRadius = outerRadius;
1009 v4->fInnerRadius = innerRadius;
1010
Brian Salomon289e3d82016-12-14 15:52:56 -05001011 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001012 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001013 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001014 v5->fOuterRadius = outerRadius;
1015 v5->fInnerRadius = innerRadius;
1016
Brian Salomon289e3d82016-12-14 15:52:56 -05001017 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001018 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001019 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001020 v6->fOuterRadius = outerRadius;
1021 v6->fInnerRadius = innerRadius;
1022
Brian Salomon289e3d82016-12-14 15:52:56 -05001023 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001024 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001025 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001026 v7->fOuterRadius = outerRadius;
1027 v7->fInnerRadius = innerRadius;
1028
1029 if (fClipPlane) {
1030 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1031 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1032 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1033 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1034 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1035 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 }
1039 int unionIdx = 1;
1040 if (fClipPlaneIsect) {
1041 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1042 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1043 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1044 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1045 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1046 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 unionIdx = 2;
1050 }
1051 if (fClipPlaneUnion) {
1052 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1053 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1054 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1055 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1056 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1057 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 }
1061 } else {
1062 // filled
1063 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1064 v8->fPos = center;
1065 v8->fColor = color;
1066 v8->fOffset = SkPoint::Make(0, 0);
1067 v8->fOuterRadius = outerRadius;
1068 v8->fInnerRadius = innerRadius;
1069 if (fClipPlane) {
1070 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1071 }
1072 int unionIdx = 1;
1073 if (fClipPlaneIsect) {
1074 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1075 unionIdx = 2;
1076 }
1077 if (fClipPlaneUnion) {
1078 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1079 }
1080 }
1081
1082 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1083 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1084 for (int i = 0; i < primIndexCount; ++i) {
1085 *indices++ = primIndices[i] + currStartVertex;
1086 }
1087
1088 currStartVertex += circle_type_to_vert_count(geom.fStroked);
Brian Salomon289e3d82016-12-14 15:52:56 -05001089 vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001090 }
jvanverth6ca48822016-10-07 06:57:32 -07001091
1092 GrMesh mesh;
1093 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1094 firstIndex, fVertCount, fIndexCount);
1095 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001096 }
1097
Brian Salomon25a88092016-12-01 09:36:50 -05001098 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001099 CircleOp* that = t->cast<CircleOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001100 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1101 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001102 return false;
1103 }
1104
jvanverth6ca48822016-10-07 06:57:32 -07001105 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001106 return false;
1107 }
1108
Brian Salomon289e3d82016-12-14 15:52:56 -05001109 // Because we've set up the ops that don't use the planes with noop values
1110 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001111 fClipPlane |= that->fClipPlane;
1112 fClipPlaneIsect |= that->fClipPlaneIsect;
1113 fClipPlaneUnion |= that->fClipPlaneUnion;
1114
bsalomoncdaa97b2016-03-08 08:30:14 -08001115 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001116 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001117 fVertCount += that->fVertCount;
1118 fIndexCount += that->fIndexCount;
1119 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001120 return true;
1121 }
1122
bsalomon4b4a7cc2016-07-08 04:42:54 -07001123 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05001124 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001125 SkScalar fInnerRadius;
1126 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 SkScalar fClipPlane[3];
1128 SkScalar fIsectPlane[3];
1129 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001130 SkRect fDevBounds;
1131 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001132 };
1133
jvanverth6ca48822016-10-07 06:57:32 -07001134 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05001135 SkMatrix fViewMatrixIfUsingLocalCoords;
1136 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 Salomondad29232016-12-01 16:40:24 -05001143 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001144};
1145
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001146///////////////////////////////////////////////////////////////////////////////
1147
Brian Salomon289e3d82016-12-14 15:52:56 -05001148class EllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001149public:
Brian Salomon25a88092016-12-01 09:36:50 -05001150 DEFINE_OP_CLASS_ID
Brian Salomon289e3d82016-12-14 15:52:56 -05001151 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
1152 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001153 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001154
bsalomon4b4a7cc2016-07-08 04:42:54 -07001155 // do any matrix crunching before we reset the draw state for device coords
1156 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1157 viewMatrix.mapPoints(&center, 1);
1158 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1159 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon289e3d82016-12-14 15:52:56 -05001160 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1161 viewMatrix[SkMatrix::kMSkewY] * ellipseYRadius);
1162 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * ellipseXRadius +
1163 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001164
bsalomon4b4a7cc2016-07-08 04:42:54 -07001165 // do (potentially) anisotropic mapping of stroke
1166 SkVector scaledStroke;
1167 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001168 scaledStroke.fX = SkScalarAbs(
1169 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1170 scaledStroke.fY = SkScalarAbs(
1171 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001172
1173 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001174 bool isStrokeOnly =
1175 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001176 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1177
1178 SkScalar innerXRadius = 0;
1179 SkScalar innerYRadius = 0;
1180 if (hasStroke) {
1181 if (SkScalarNearlyZero(scaledStroke.length())) {
1182 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1183 } else {
1184 scaledStroke.scale(SK_ScalarHalf);
1185 }
1186
1187 // we only handle thick strokes for near-circular ellipses
1188 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001189 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001190 return nullptr;
1191 }
1192
1193 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001194 if (scaledStroke.fX * (yRadius * yRadius) <
1195 (scaledStroke.fY * scaledStroke.fY) * xRadius ||
1196 scaledStroke.fY * (xRadius * xRadius) <
1197 (scaledStroke.fX * scaledStroke.fX) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001198 return nullptr;
1199 }
1200
1201 // this is legit only if scale & translation (which should be the case at the moment)
1202 if (isStrokeOnly) {
1203 innerXRadius = xRadius - scaledStroke.fX;
1204 innerYRadius = yRadius - scaledStroke.fY;
1205 }
1206
1207 xRadius += scaledStroke.fX;
1208 yRadius += scaledStroke.fY;
1209 }
1210
Brian Salomon289e3d82016-12-14 15:52:56 -05001211 sk_sp<EllipseOp> op(new EllipseOp());
1212 op->fGeoData.emplace_back(
1213 Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
1214 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1215 center.fX + xRadius, center.fY + yRadius)});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001216
Brian Salomon289e3d82016-12-14 15:52:56 -05001217 op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001218
bsalomon4b4a7cc2016-07-08 04:42:54 -07001219 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon289e3d82016-12-14 15:52:56 -05001220 op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001221
Brian Salomon289e3d82016-12-14 15:52:56 -05001222 op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1223 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
1224 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -08001225 }
joshualitt76e7fb62015-02-11 08:52:27 -08001226
Brian Salomon289e3d82016-12-14 15:52:56 -05001227 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001228
Brian Salomon7c3e7182016-12-01 09:35:30 -05001229 SkString dumpInfo() const override {
1230 SkString string;
1231 string.appendf("Stroked: %d\n", fStroked);
1232 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001233 string.appendf(
1234 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1235 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1236 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1237 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1238 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001239 }
1240 string.append(DumpPipelineInfo(*this->pipeline()));
1241 string.append(INHERITED::dumpInfo());
1242 return string;
1243 }
1244
bsalomone46f9fe2015-08-18 06:05:14 -07001245private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001246 EllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001247
Brian Salomon92aee3d2016-12-21 09:20:25 -05001248 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
1249 input->pipelineColorInput()->setKnownFourComponents(fGeoData[0].fColor);
1250 input->pipelineCoverageInput()->setUnknownSingleComponent();
1251 }
1252
1253 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1254 if (!optimizations.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001255 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001256 }
Brian Salomon92aee3d2016-12-21 09:20:25 -05001257 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001258 fViewMatrixIfUsingLocalCoords.reset();
1259 }
joshualitt76e7fb62015-02-11 08:52:27 -08001260 }
1261
joshualitt144c3c82015-11-30 12:30:13 -08001262 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001263 SkMatrix localMatrix;
1264 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001265 return;
1266 }
1267
1268 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001269 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001270
joshualitt76e7fb62015-02-11 08:52:27 -08001271 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001272 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001273 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001274 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon289e3d82016-12-14 15:52:56 -05001275 EllipseVertex* verts =
1276 reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001277 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001278 return;
1279 }
1280
bsalomon8415abe2015-05-04 11:41:41 -07001281 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001282 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001283
brianosmanbb2ff942016-02-11 14:15:18 -08001284 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001285 SkScalar xRadius = geom.fXRadius;
1286 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001287
1288 // Compute the reciprocals of the radii here to save time in the shader
1289 SkScalar xRadRecip = SkScalarInvert(xRadius);
1290 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001291 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1292 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001293
bsalomonb5238a72015-05-05 07:49:49 -07001294 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001295
vjiaoblack977996d2016-06-30 12:20:54 -07001296 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1297 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1298 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1299
joshualitt76e7fb62015-02-11 08:52:27 -08001300 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon289e3d82016-12-14 15:52:56 -05001301 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001302 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001303 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001304 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1305 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1306
Brian Salomon289e3d82016-12-14 15:52:56 -05001307 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001308 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001309 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001310 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1311 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1312
1313 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001314 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001315 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001316 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1317 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1318
1319 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001320 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001321 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001322 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1323 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1324
bsalomonb5238a72015-05-05 07:49:49 -07001325 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001326 }
Hal Canary144caf52016-11-07 17:57:18 -05001327 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001328 }
1329
Brian Salomon25a88092016-12-01 09:36:50 -05001330 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001331 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001332
1333 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1334 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001335 return false;
1336 }
1337
bsalomoncdaa97b2016-03-08 08:30:14 -08001338 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001339 return false;
1340 }
1341
bsalomoncdaa97b2016-03-08 08:30:14 -08001342 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001343 return false;
1344 }
1345
bsalomoncdaa97b2016-03-08 08:30:14 -08001346 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001347 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001348 return true;
1349 }
1350
bsalomon4b4a7cc2016-07-08 04:42:54 -07001351 struct Geometry {
1352 GrColor fColor;
1353 SkScalar fXRadius;
1354 SkScalar fYRadius;
1355 SkScalar fInnerXRadius;
1356 SkScalar fInnerYRadius;
1357 SkRect fDevBounds;
1358 };
joshualitt76e7fb62015-02-11 08:52:27 -08001359
Brian Salomon289e3d82016-12-14 15:52:56 -05001360 bool fStroked;
1361 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001362 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001363
Brian Salomondad29232016-12-01 16:40:24 -05001364 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001365};
1366
joshualitt76e7fb62015-02-11 08:52:27 -08001367/////////////////////////////////////////////////////////////////////////////////////////////////
1368
Brian Salomon289e3d82016-12-14 15:52:56 -05001369class DIEllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001370public:
Brian Salomon25a88092016-12-01 09:36:50 -05001371 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001372
Brian Salomon289e3d82016-12-14 15:52:56 -05001373 static sk_sp<GrDrawOp> Make(GrColor color,
1374 const SkMatrix& viewMatrix,
1375 const SkRect& ellipse,
1376 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001377 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1378 SkScalar xRadius = SkScalarHalf(ellipse.width());
1379 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001380
bsalomon4b4a7cc2016-07-08 04:42:54 -07001381 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001382 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
1383 ? DIEllipseStyle::kStroke
1384 : (SkStrokeRec::kHairline_Style == style)
1385 ? DIEllipseStyle::kHairline
1386 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001387
1388 SkScalar innerXRadius = 0;
1389 SkScalar innerYRadius = 0;
1390 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1391 SkScalar strokeWidth = stroke.getWidth();
1392
1393 if (SkScalarNearlyZero(strokeWidth)) {
1394 strokeWidth = SK_ScalarHalf;
1395 } else {
1396 strokeWidth *= SK_ScalarHalf;
1397 }
1398
1399 // we only handle thick strokes for near-circular ellipses
1400 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001401 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001402 return nullptr;
1403 }
1404
1405 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001406 if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
1407 strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001408 return nullptr;
1409 }
1410
1411 // set inner radius (if needed)
1412 if (SkStrokeRec::kStroke_Style == style) {
1413 innerXRadius = xRadius - strokeWidth;
1414 innerYRadius = yRadius - strokeWidth;
1415 }
1416
1417 xRadius += strokeWidth;
1418 yRadius += strokeWidth;
1419 }
1420 if (DIEllipseStyle::kStroke == dieStyle) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001421 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
1422 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001423 }
1424
1425 // This expands the outer rect so that after CTM we end up with a half-pixel border
1426 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1427 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1428 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1429 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001430 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1431 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001432
Brian Salomon289e3d82016-12-14 15:52:56 -05001433 sk_sp<DIEllipseOp> op(new DIEllipseOp());
1434 op->fGeoData.emplace_back(Geometry{
1435 viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
1436 dieStyle,
1437 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1438 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
1439 op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1440 IsZeroArea::kNo);
1441 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08001442 }
1443
Brian Salomon289e3d82016-12-14 15:52:56 -05001444 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001445
Brian Salomon7c3e7182016-12-01 09:35:30 -05001446 SkString dumpInfo() const override {
1447 SkString string;
1448 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001449 string.appendf(
1450 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1451 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1452 "GeoDY: %.2f\n",
1453 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1454 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1455 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001456 }
1457 string.append(DumpPipelineInfo(*this->pipeline()));
1458 string.append(INHERITED::dumpInfo());
1459 return string;
1460 }
1461
bsalomone46f9fe2015-08-18 06:05:14 -07001462private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001463 DIEllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001464
Brian Salomon92aee3d2016-12-21 09:20:25 -05001465 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
1466 input->pipelineColorInput()->setKnownFourComponents(fGeoData[0].fColor);
1467 input->pipelineCoverageInput()->setUnknownSingleComponent();
1468 }
1469
1470 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1471 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1472 fUsesLocalCoords = optimizations.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001473 }
1474
joshualitt144c3c82015-11-30 12:30:13 -08001475 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001476 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001477 sk_sp<GrGeometryProcessor> gp(
1478 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001479
joshualitt76e7fb62015-02-11 08:52:27 -08001480 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001481 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001482 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001483 QuadHelper helper;
1484 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05001485 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001486 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001487 return;
1488 }
1489
joshualitt76e7fb62015-02-11 08:52:27 -08001490 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001491 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001492
brianosmanbb2ff942016-02-11 14:15:18 -08001493 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001494 SkScalar xRadius = geom.fXRadius;
1495 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001496
bsalomonb5238a72015-05-05 07:49:49 -07001497 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001498
1499 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001500 SkScalar offsetDx = geom.fGeoDx / xRadius;
1501 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001502
reed80ea19c2015-05-12 10:37:34 -07001503 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1504 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001505
1506 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001507 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001508 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1509 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1510
Brian Salomon289e3d82016-12-14 15:52:56 -05001511 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001512 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001513 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1514 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1515
1516 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001517 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001518 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1519 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1520
1521 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001522 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001523 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1524 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1525
bsalomonb5238a72015-05-05 07:49:49 -07001526 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001527 }
Hal Canary144caf52016-11-07 17:57:18 -05001528 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001529 }
halcanary9d524f22016-03-29 09:03:52 -07001530
Brian Salomon25a88092016-12-01 09:36:50 -05001531 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001532 DIEllipseOp* that = t->cast<DIEllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001533 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1534 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001535 return false;
1536 }
1537
bsalomoncdaa97b2016-03-08 08:30:14 -08001538 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001539 return false;
1540 }
1541
joshualittd96a67b2015-05-05 14:09:05 -07001542 // TODO rewrite to allow positioning on CPU
1543 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001544 return false;
1545 }
1546
bsalomoncdaa97b2016-03-08 08:30:14 -08001547 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001548 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001549 return true;
1550 }
1551
joshualitt76e7fb62015-02-11 08:52:27 -08001552 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001553 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001554
bsalomon4b4a7cc2016-07-08 04:42:54 -07001555 struct Geometry {
1556 SkMatrix fViewMatrix;
1557 GrColor fColor;
1558 SkScalar fXRadius;
1559 SkScalar fYRadius;
1560 SkScalar fInnerXRadius;
1561 SkScalar fInnerYRadius;
1562 SkScalar fGeoDx;
1563 SkScalar fGeoDy;
1564 DIEllipseStyle fStyle;
1565 SkRect fBounds;
1566 };
1567
Brian Salomon289e3d82016-12-14 15:52:56 -05001568 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001569 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001570
Brian Salomondad29232016-12-01 16:40:24 -05001571 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001572};
1573
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001574///////////////////////////////////////////////////////////////////////////////
1575
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001576// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001577//
1578// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1579// ____________
1580// |_|________|_|
1581// | | | |
1582// | | | |
1583// | | | |
1584// |_|________|_|
1585// |_|________|_|
1586//
1587// For strokes, we don't draw the center quad.
1588//
1589// For circular roundrects, in the case where the stroke width is greater than twice
1590// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001591// in the center. The shared vertices are duplicated so we can set a different outer radius
1592// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001593// ____________
1594// |_|________|_|
1595// | |\ ____ /| |
1596// | | | | | |
1597// | | |____| | |
1598// |_|/______\|_|
1599// |_|________|_|
1600//
1601// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001602//
1603// For filled rrects that need to provide a distance vector we resuse the overstroke
1604// geometry but make the inner rect degenerate (either a point or a horizontal or
1605// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001606
jvanverth84839f62016-08-29 10:16:40 -07001607static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001608 // clang-format off
1609 // overstroke quads
1610 // we place this at the beginning so that we can skip these indices when rendering normally
1611 16, 17, 19, 16, 19, 18,
1612 19, 17, 23, 19, 23, 21,
1613 21, 23, 22, 21, 22, 20,
1614 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001615
Brian Salomon289e3d82016-12-14 15:52:56 -05001616 // corners
1617 0, 1, 5, 0, 5, 4,
1618 2, 3, 7, 2, 7, 6,
1619 8, 9, 13, 8, 13, 12,
1620 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001621
Brian Salomon289e3d82016-12-14 15:52:56 -05001622 // edges
1623 1, 2, 6, 1, 6, 5,
1624 4, 5, 9, 4, 9, 8,
1625 6, 7, 11, 6, 11, 10,
1626 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001627
Brian Salomon289e3d82016-12-14 15:52:56 -05001628 // center
1629 // we place this at the end so that we can ignore these indices when not rendering as filled
1630 5, 6, 10, 5, 10, 9,
1631 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001632};
Brian Salomon289e3d82016-12-14 15:52:56 -05001633
jvanverth84839f62016-08-29 10:16:40 -07001634// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001635static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001636
jvanverth84839f62016-08-29 10:16:40 -07001637// overstroke count is arraysize minus the center indices
1638static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1639// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001640static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001641// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001642static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1643static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001644static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001645
jvanverthc3d0e422016-08-25 08:12:35 -07001646enum RRectType {
1647 kFill_RRectType,
1648 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001649 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001650 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001651};
1652
jvanverth84839f62016-08-29 10:16:40 -07001653static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001654 switch (type) {
1655 case kFill_RRectType:
1656 case kStroke_RRectType:
1657 return kVertsPerStandardRRect;
1658 case kOverstroke_RRectType:
1659 case kFillWithDist_RRectType:
1660 return kVertsPerOverstrokeRRect;
1661 }
1662 SkFAIL("Invalid type");
1663 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001664}
1665
1666static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001667 switch (type) {
1668 case kFill_RRectType:
1669 return kIndicesPerFillRRect;
1670 case kStroke_RRectType:
1671 return kIndicesPerStrokeRRect;
1672 case kOverstroke_RRectType:
1673 case kFillWithDist_RRectType:
1674 return kIndicesPerOverstrokeRRect;
1675 }
1676 SkFAIL("Invalid type");
1677 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001678}
1679
1680static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001681 switch (type) {
1682 case kFill_RRectType:
1683 case kStroke_RRectType:
1684 return gStandardRRectIndices;
1685 case kOverstroke_RRectType:
1686 case kFillWithDist_RRectType:
1687 return gOverstrokeRRectIndices;
1688 }
1689 SkFAIL("Invalid type");
1690 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001691}
1692
joshualitt76e7fb62015-02-11 08:52:27 -08001693///////////////////////////////////////////////////////////////////////////////////////////////////
1694
Robert Phillips79839d42016-10-06 15:03:34 -04001695// For distance computations in the interior of filled rrects we:
1696//
1697// add a interior degenerate (point or line) rect
1698// each vertex of that rect gets -outerRad as its radius
1699// this makes the computation of the distance to the outer edge be negative
1700// negative values are caught and then handled differently in the GP's onEmitCode
1701// each vertex is also given the normalized x & y distance from the interior rect's edge
1702// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1703
Brian Salomon289e3d82016-12-14 15:52:56 -05001704class CircularRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001705public:
Brian Salomon25a88092016-12-01 09:36:50 -05001706 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001707
bsalomon4b4a7cc2016-07-08 04:42:54 -07001708 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1709 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05001710 CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1711 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1712 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001713 SkRect bounds = devRect;
1714 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1715 SkScalar innerRadius = 0.0f;
1716 SkScalar outerRadius = devRadius;
1717 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001718 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001719 if (devStrokeWidth > 0) {
1720 if (SkScalarNearlyZero(devStrokeWidth)) {
1721 halfWidth = SK_ScalarHalf;
1722 } else {
1723 halfWidth = SkScalarHalf(devStrokeWidth);
1724 }
joshualitt76e7fb62015-02-11 08:52:27 -08001725
bsalomon4b4a7cc2016-07-08 04:42:54 -07001726 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001727 // Outset stroke by 1/4 pixel
1728 devStrokeWidth += 0.25f;
1729 // If stroke is greater than width or height, this is still a fill
1730 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001731 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001732 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001733 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001734 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001735 }
1736 outerRadius += halfWidth;
1737 bounds.outset(halfWidth, halfWidth);
1738 }
Robert Phillips79839d42016-10-06 15:03:34 -04001739 if (kFill_RRectType == type && needsDistance) {
1740 type = kFillWithDist_RRectType;
1741 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001742
bsalomon4b4a7cc2016-07-08 04:42:54 -07001743 // The radii are outset for two reasons. First, it allows the shader to simply perform
1744 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1745 // Second, the outer radius is used to compute the verts of the bounding box that is
1746 // rendered and the outset ensures the box will cover all partially covered by the rrect
1747 // corners.
1748 outerRadius += SK_ScalarHalf;
1749 innerRadius -= SK_ScalarHalf;
1750
bsalomon88cf17d2016-07-08 06:40:56 -07001751 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1752
1753 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001754 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1755
Brian Salomon289e3d82016-12-14 15:52:56 -05001756 fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001757 fVertCount = rrect_type_to_vert_count(type);
1758 fIndexCount = rrect_type_to_index_count(type);
1759 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001760 }
1761
Brian Salomon289e3d82016-12-14 15:52:56 -05001762 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001763
jvanverthc3d0e422016-08-25 08:12:35 -07001764 SkString dumpInfo() const override {
1765 SkString string;
1766 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001767 string.appendf(
1768 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1769 "InnerRad: %.2f, OuterRad: %.2f\n",
1770 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1771 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1772 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001773 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001774 string.append(DumpPipelineInfo(*this->pipeline()));
jvanverthc3d0e422016-08-25 08:12:35 -07001775 string.append(INHERITED::dumpInfo());
1776 return string;
1777 }
1778
Brian Salomon92aee3d2016-12-21 09:20:25 -05001779private:
1780 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
1781 input->pipelineColorInput()->setKnownFourComponents(fGeoData[0].fColor);
1782 input->pipelineCoverageInput()->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001783 }
1784
Brian Salomon92aee3d2016-12-21 09:20:25 -05001785 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1786 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1787 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001788 fViewMatrixIfUsingLocalCoords.reset();
1789 }
joshualitt76e7fb62015-02-11 08:52:27 -08001790 }
1791
Robert Phillips79839d42016-10-06 15:03:34 -04001792 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001793 SkPoint fPos;
1794 GrColor fColor;
1795 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001796 SkScalar fOuterRadius;
1797 SkScalar fInnerRadius;
1798 // No half plane, we don't use it here.
1799 };
1800
Brian Salomon289e3d82016-12-14 15:52:56 -05001801 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1802 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1803 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001804 SkASSERT(smInset < bigInset);
1805
1806 // TL
1807 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1808 (*verts)->fColor = color;
1809 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1810 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001811 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001812 (*verts)++;
1813
1814 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001815 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001816 (*verts)->fColor = color;
1817 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1818 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001819 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001820 (*verts)++;
1821
Brian Salomon289e3d82016-12-14 15:52:56 -05001822 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001823 (*verts)->fColor = color;
1824 (*verts)->fOffset = SkPoint::Make(0, 0);
1825 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001826 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001827 (*verts)++;
1828
Brian Salomon289e3d82016-12-14 15:52:56 -05001829 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001830 (*verts)->fColor = color;
1831 (*verts)->fOffset = SkPoint::Make(0, 0);
1832 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001833 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001834 (*verts)++;
1835
1836 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1837 (*verts)->fColor = color;
1838 (*verts)->fOffset = SkPoint::Make(0, 0);
1839 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001840 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001841 (*verts)++;
1842
1843 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1844 (*verts)->fColor = color;
1845 (*verts)->fOffset = SkPoint::Make(0, 0);
1846 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001847 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001848 (*verts)++;
1849
1850 // BL
1851 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1852 (*verts)->fColor = color;
1853 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1854 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001855 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001856 (*verts)++;
1857
1858 // BR
1859 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1860 (*verts)->fColor = color;
1861 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1862 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001863 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001864 (*verts)++;
1865 }
1866
joshualitt144c3c82015-11-30 12:30:13 -08001867 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001868 // Invert the view matrix as a local matrix (if any other processors require coords).
1869 SkMatrix localMatrix;
1870 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001871 return;
1872 }
1873
1874 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001875 sk_sp<GrGeometryProcessor> gp(
1876 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001877
joshualitt76e7fb62015-02-11 08:52:27 -08001878 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001879 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001880 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001881
jvanverth84839f62016-08-29 10:16:40 -07001882 const GrBuffer* vertexBuffer;
1883 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001884
Brian Salomon289e3d82016-12-14 15:52:56 -05001885 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1886 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001887 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001888 SkDebugf("Could not allocate vertices\n");
1889 return;
1890 }
1891
jvanverth84839f62016-08-29 10:16:40 -07001892 const GrBuffer* indexBuffer = nullptr;
1893 int firstIndex = 0;
1894 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1895 if (!indices) {
1896 SkDebugf("Could not allocate indices\n");
1897 return;
1898 }
1899
1900 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001901 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001902 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001903
brianosmanbb2ff942016-02-11 14:15:18 -08001904 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001905 SkScalar outerRadius = args.fOuterRadius;
1906
egdanielbc227142015-04-21 06:28:08 -07001907 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001908
Brian Salomon289e3d82016-12-14 15:52:56 -05001909 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1910 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001911
Brian Salomon289e3d82016-12-14 15:52:56 -05001912 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001913 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001914 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomon289e3d82016-12-14 15:52:56 -05001915 SkScalar innerRadius =
1916 args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
1917 ? args.fInnerRadius / args.fOuterRadius
1918 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001919 for (int i = 0; i < 4; ++i) {
1920 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001921 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001922 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1923 verts->fOuterRadius = outerRadius;
1924 verts->fInnerRadius = innerRadius;
1925 verts++;
1926
1927 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001928 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001929 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1930 verts->fOuterRadius = outerRadius;
1931 verts->fInnerRadius = innerRadius;
1932 verts++;
1933
1934 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001935 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001936 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1937 verts->fOuterRadius = outerRadius;
1938 verts->fInnerRadius = innerRadius;
1939 verts++;
1940
1941 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001942 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001943 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1944 verts->fOuterRadius = outerRadius;
1945 verts->fInnerRadius = innerRadius;
1946 verts++;
1947 }
jvanverthc3d0e422016-08-25 08:12:35 -07001948 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001949 // Effectively this is an additional stroked rrect, with its
1950 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1951 // This will give us correct AA in the center and the correct
1952 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001953 //
jvanvertha4f1af82016-08-29 07:17:47 -07001954 // Also, the outer offset is a constant vector pointing to the right, which
1955 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001956 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001957 SkASSERT(args.fInnerRadius <= 0.0f);
1958
jvanvertha4f1af82016-08-29 07:17:47 -07001959 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1960 // this is the normalized distance from the outer rectangle of this
1961 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001962 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001963
Brian Salomon289e3d82016-12-14 15:52:56 -05001964 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1965 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001966 }
jvanverth6a397612016-08-26 08:15:33 -07001967
Robert Phillips79839d42016-10-06 15:03:34 -04001968 if (kFillWithDist_RRectType == args.fType) {
1969 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001970
Robert Phillips79839d42016-10-06 15:03:34 -04001971 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001972
Brian Salomon289e3d82016-12-14 15:52:56 -05001973 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
1974 -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001975 }
jvanverth84839f62016-08-29 10:16:40 -07001976
1977 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1978 const int primIndexCount = rrect_type_to_index_count(args.fType);
1979 for (int i = 0; i < primIndexCount; ++i) {
1980 *indices++ = primIndices[i] + currStartVertex;
1981 }
1982
1983 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001984 }
1985
jvanverth84839f62016-08-29 10:16:40 -07001986 GrMesh mesh;
1987 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1988 firstIndex, fVertCount, fIndexCount);
1989 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001990 }
1991
Brian Salomon25a88092016-12-01 09:36:50 -05001992 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001993 CircularRRectOp* that = t->cast<CircularRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001994 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1995 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001996 return false;
1997 }
1998
bsalomoncdaa97b2016-03-08 08:30:14 -08001999 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002000 return false;
2001 }
2002
bsalomoncdaa97b2016-03-08 08:30:14 -08002003 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002004 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002005 fVertCount += that->fVertCount;
2006 fIndexCount += that->fIndexCount;
2007 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002008 return true;
2009 }
2010
bsalomon4b4a7cc2016-07-08 04:42:54 -07002011 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05002012 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002013 SkScalar fInnerRadius;
2014 SkScalar fOuterRadius;
2015 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002016 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002017 };
2018
joshualitt76e7fb62015-02-11 08:52:27 -08002019 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05002020 SkMatrix fViewMatrixIfUsingLocalCoords;
2021 int fVertCount;
2022 int fIndexCount;
2023 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002024
Brian Salomondad29232016-12-01 16:40:24 -05002025 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002026};
2027
jvanverth84839f62016-08-29 10:16:40 -07002028static const int kNumRRectsInIndexBuffer = 256;
2029
2030GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2031GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2032static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2033 GrResourceProvider* resourceProvider) {
2034 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2035 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2036 switch (type) {
2037 case kFill_RRectType:
2038 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002039 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2040 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002041 case kStroke_RRectType:
2042 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002043 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2044 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002045 default:
2046 SkASSERT(false);
2047 return nullptr;
2048 };
2049}
2050
Brian Salomon289e3d82016-12-14 15:52:56 -05002051class EllipticalRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08002052public:
Brian Salomon25a88092016-12-01 09:36:50 -05002053 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002054
bsalomon4b4a7cc2016-07-08 04:42:54 -07002055 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2056 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05002057 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
2058 float devXRadius, float devYRadius, SkVector devStrokeWidths,
2059 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002060 SkASSERT(devXRadius > 0.5);
2061 SkASSERT(devYRadius > 0.5);
2062 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2063 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2064 SkScalar innerXRadius = 0.0f;
2065 SkScalar innerYRadius = 0.0f;
2066 SkRect bounds = devRect;
2067 bool stroked = false;
2068 if (devStrokeWidths.fX > 0) {
2069 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2070 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2071 } else {
2072 devStrokeWidths.scale(SK_ScalarHalf);
2073 }
joshualitt76e7fb62015-02-11 08:52:27 -08002074
bsalomon4b4a7cc2016-07-08 04:42:54 -07002075 // we only handle thick strokes for near-circular ellipses
2076 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002077 (SK_ScalarHalf * devXRadius > devYRadius ||
2078 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002079 return nullptr;
2080 }
2081
2082 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002083 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2084 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002085 return nullptr;
2086 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002087 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2088 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002089 return nullptr;
2090 }
2091
2092 // this is legit only if scale & translation (which should be the case at the moment)
2093 if (strokeOnly) {
2094 innerXRadius = devXRadius - devStrokeWidths.fX;
2095 innerYRadius = devYRadius - devStrokeWidths.fY;
2096 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2097 }
2098
2099 devXRadius += devStrokeWidths.fX;
2100 devYRadius += devStrokeWidths.fY;
2101 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2102 }
2103
Brian Salomon289e3d82016-12-14 15:52:56 -05002104 sk_sp<EllipticalRRectOp> op(new EllipticalRRectOp());
2105 op->fStroked = stroked;
2106 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
2107 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002108 // Expand the rect for aa in order to generate the correct vertices.
2109 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon289e3d82016-12-14 15:52:56 -05002110 op->fGeoData.emplace_back(
2111 Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2112 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08002113 }
2114
Brian Salomon289e3d82016-12-14 15:52:56 -05002115 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002116
Brian Salomon7c3e7182016-12-01 09:35:30 -05002117 SkString dumpInfo() const override {
2118 SkString string;
2119 string.appendf("Stroked: %d\n", fStroked);
2120 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002121 string.appendf(
2122 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2123 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2124 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2125 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2126 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002127 }
2128 string.append(DumpPipelineInfo(*this->pipeline()));
2129 string.append(INHERITED::dumpInfo());
2130 return string;
2131 }
2132
bsalomone46f9fe2015-08-18 06:05:14 -07002133private:
Brian Salomon289e3d82016-12-14 15:52:56 -05002134 EllipticalRRectOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07002135
Brian Salomon92aee3d2016-12-21 09:20:25 -05002136 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override {
2137 input->pipelineColorInput()->setKnownFourComponents(fGeoData[0].fColor);
2138 input->pipelineCoverageInput()->setUnknownSingleComponent();
2139 }
2140
2141 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
2142 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
2143 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08002144 fViewMatrixIfUsingLocalCoords.reset();
2145 }
joshualitt76e7fb62015-02-11 08:52:27 -08002146 }
2147
joshualitt144c3c82015-11-30 12:30:13 -08002148 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002149 SkMatrix localMatrix;
2150 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002151 return;
2152 }
2153
2154 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002155 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002156
joshualitt76e7fb62015-02-11 08:52:27 -08002157 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002158 size_t vertexStride = gp->getVertexStride();
2159 SkASSERT(vertexStride == sizeof(EllipseVertex));
2160
bsalomonb5238a72015-05-05 07:49:49 -07002161 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002162 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002163 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2164 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002165
bsalomonb5238a72015-05-05 07:49:49 -07002166 InstancedHelper helper;
2167 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05002168 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
2169 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002170 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002171 SkDebugf("Could not allocate vertices\n");
2172 return;
2173 }
2174
joshualitt76e7fb62015-02-11 08:52:27 -08002175 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002176 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002177
brianosmanbb2ff942016-02-11 14:15:18 -08002178 GrColor color = args.fColor;
2179
joshualitt76e7fb62015-02-11 08:52:27 -08002180 // Compute the reciprocals of the radii here to save time in the shader
2181 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2182 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2183 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2184 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2185
2186 // Extend the radii out half a pixel to antialias.
2187 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2188 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2189
egdanielbc227142015-04-21 06:28:08 -07002190 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002191
Brian Salomon289e3d82016-12-14 15:52:56 -05002192 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2193 bounds.fBottom - yOuterRadius, bounds.fBottom};
2194 SkScalar yOuterOffsets[4] = {yOuterRadius,
2195 SK_ScalarNearlyZero, // we're using inversesqrt() in
2196 // shader, so can't be exactly 0
2197 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002198
2199 for (int i = 0; i < 4; ++i) {
2200 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002201 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002202 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2203 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2204 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2205 verts++;
2206
2207 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002208 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002209 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2210 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2211 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2212 verts++;
2213
2214 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002215 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002216 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2217 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2218 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2219 verts++;
2220
2221 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002222 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002223 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2224 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2225 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2226 verts++;
2227 }
2228 }
Hal Canary144caf52016-11-07 17:57:18 -05002229 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08002230 }
2231
Brian Salomon25a88092016-12-01 09:36:50 -05002232 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002233 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002234
2235 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2236 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002237 return false;
2238 }
2239
bsalomoncdaa97b2016-03-08 08:30:14 -08002240 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002241 return false;
2242 }
2243
bsalomoncdaa97b2016-03-08 08:30:14 -08002244 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002245 return false;
2246 }
2247
bsalomoncdaa97b2016-03-08 08:30:14 -08002248 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002249 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002250 return true;
2251 }
2252
bsalomon4b4a7cc2016-07-08 04:42:54 -07002253 struct Geometry {
2254 GrColor fColor;
2255 SkScalar fXRadius;
2256 SkScalar fYRadius;
2257 SkScalar fInnerXRadius;
2258 SkScalar fInnerYRadius;
2259 SkRect fDevBounds;
2260 };
2261
Brian Salomon289e3d82016-12-14 15:52:56 -05002262 bool fStroked;
2263 SkMatrix fViewMatrixIfUsingLocalCoords;
2264 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002265
Brian Salomondad29232016-12-01 16:40:24 -05002266 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002267};
2268
Brian Salomon289e3d82016-12-14 15:52:56 -05002269static sk_sp<GrDrawOp> make_rrect_op(GrColor color,
2270 bool needsDistance,
2271 const SkMatrix& viewMatrix,
2272 const SkRRect& rrect,
2273 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002274 SkASSERT(viewMatrix.rectStaysRect());
2275 SkASSERT(rrect.isSimple());
2276 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002277
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002278 // RRect ops only handle simple, but not too simple, rrects.
2279 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002280 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002281 SkRect bounds;
2282 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002283
2284 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002285 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2286 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2287 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2288 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002289
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002290 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002291
bsalomon4b4a7cc2016-07-08 04:42:54 -07002292 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2293 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002294 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002295
Brian Salomon289e3d82016-12-14 15:52:56 -05002296 bool isStrokeOnly =
2297 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002298 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2299
jvanverthc3d0e422016-08-25 08:12:35 -07002300 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002301 if (hasStroke) {
2302 if (SkStrokeRec::kHairline_Style == style) {
2303 scaledStroke.set(1, 1);
2304 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002305 scaledStroke.fX = SkScalarAbs(
2306 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2307 scaledStroke.fY = SkScalarAbs(
2308 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002309 }
2310
jvanverthc3d0e422016-08-25 08:12:35 -07002311 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2312 // for non-circular rrects, if half of strokewidth is greater than radius,
2313 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002314 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2315 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002316 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002317 }
2318 }
2319
2320 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2321 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2322 // patch will have fractional coverage. This only matters when the interior is actually filled.
2323 // We could consider falling back to rect rendering here, since a tiny radius is
2324 // indistinguishable from a square corner.
2325 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002326 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002327 }
2328
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002329 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002330 if (isCircular) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002331 return sk_sp<GrDrawOp>(new CircularRRectOp(color, needsDistance, viewMatrix, bounds,
2332 xRadius, scaledStroke.fX, isStrokeOnly));
2333 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002334 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002335 return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
2336 isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002337 }
joshualitt3e708c52015-04-30 13:49:27 -07002338}
2339
Brian Salomon289e3d82016-12-14 15:52:56 -05002340sk_sp<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
2341 bool needsDistance,
2342 const SkMatrix& viewMatrix,
2343 const SkRRect& rrect,
2344 const SkStrokeRec& stroke,
2345 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002346 if (rrect.isOval()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002347 return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002348 }
2349
2350 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002351 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002352 }
2353
Brian Salomon289e3d82016-12-14 15:52:56 -05002354 return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002355}
joshualitt3e708c52015-04-30 13:49:27 -07002356
bsalomon4b4a7cc2016-07-08 04:42:54 -07002357///////////////////////////////////////////////////////////////////////////////
2358
Brian Salomon289e3d82016-12-14 15:52:56 -05002359sk_sp<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
2360 const SkMatrix& viewMatrix,
2361 const SkRect& oval,
2362 const SkStrokeRec& stroke,
2363 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002364 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002365 SkScalar width = oval.width();
2366 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2367 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002368 return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002369 }
2370
2371 // if we have shader derivative support, render as device-independent
2372 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002373 return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002374 }
2375
2376 // otherwise axis-aligned ellipses only
2377 if (viewMatrix.rectStaysRect()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002378 return EllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002379 }
2380
2381 return nullptr;
2382}
2383
2384///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002385
Brian Salomon289e3d82016-12-14 15:52:56 -05002386sk_sp<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrColor color, const SkMatrix& viewMatrix,
2387 const SkRect& oval, SkScalar startAngle,
2388 SkScalar sweepAngle, bool useCenter,
2389 const GrStyle& style, const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002390 SkASSERT(!oval.isEmpty());
2391 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002392 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002393 if (SkScalarAbs(sweepAngle) >= 360.f) {
2394 return nullptr;
2395 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002396 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2397 return nullptr;
2398 }
2399 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002400 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2401 useCenter};
2402 return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002403}
2404
2405///////////////////////////////////////////////////////////////////////////////
2406
joshualitt3e708c52015-04-30 13:49:27 -07002407#ifdef GR_TEST_UTILS
2408
Brian Salomon5ec9def2016-12-20 15:34:05 -05002409DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002410 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002411 SkScalar rotate = random->nextSScalar1() * 360.f;
2412 SkScalar translateX = random->nextSScalar1() * 1000.f;
2413 SkScalar translateY = random->nextSScalar1() * 1000.f;
2414 SkScalar scale = random->nextSScalar1() * 100.f;
2415 SkMatrix viewMatrix;
2416 viewMatrix.setRotate(rotate);
2417 viewMatrix.postTranslate(translateX, translateY);
2418 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002419 GrColor color = GrRandomColor(random);
2420 SkRect circle = GrTest::TestSquare(random);
2421 SkPoint center = {circle.centerX(), circle.centerY()};
2422 SkScalar radius = circle.width() / 2.f;
2423 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002424 CircleOp::ArcParams arcParamsTmp;
2425 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002426 if (random->nextBool()) {
2427 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002428 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2429 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002430 arcParams = &arcParamsTmp;
2431 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002432 sk_sp<GrDrawOp> op = CircleOp::Make(color, viewMatrix, center, radius,
2433 GrStyle(stroke, nullptr), arcParams);
2434 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002435 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002436 }
2437 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002438}
2439
Brian Salomon5ec9def2016-12-20 15:34:05 -05002440DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002441 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2442 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002443 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002444 return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002445}
2446
Brian Salomon5ec9def2016-12-20 15:34:05 -05002447DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002448 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2449 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002450 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002451 return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002452}
2453
Brian Salomon5ec9def2016-12-20 15:34:05 -05002454DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002455 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2456 GrColor color = GrRandomColor(random);
2457 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002458 bool needsDistance = random->nextBool();
Brian Salomon5ec9def2016-12-20 15:34:05 -05002459 return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002460}
2461
2462#endif