blob: 8ab33f5bdc71022549a8a86797f1fb2aa8d4a133 [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"
Brian Salomon742e31d2016-12-07 17:06:19 -050012#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080013#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070014#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050015#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080019#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080020#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070021#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080022#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080023#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050024#include "glsl/GrGLSLVarying.h"
25#include "glsl/GrGLSLVertexShaderBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050026#include "ops/GrMeshDrawOp.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
Brian Salomon25a88092016-12-01 09:36:50 -050028// TODO(joshualitt) - Break this file up during GrOp post implementation cleanup
joshualitt76e7fb62015-02-11 08:52:27 -080029
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050033 SkPoint fPos;
34 GrColor fColor;
35 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050041 SkPoint fPos;
42 GrColor fColor;
43 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
Brian Salomon289e3d82016-12-14 15:52:56 -050047static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000048}
49
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000050///////////////////////////////////////////////////////////////////////////////
51
52/**
bsalomonce1c8862014-12-15 07:11:22 -080053 * The output of this effect is a modulation of the input color and coverage for a circle. It
54 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080057 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080058 * vec4f : (p.xy, outerRad, innerRad)
59 * p is the position in the normalized space.
60 * outerRad is the outerRadius in device space.
61 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040062 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070063 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
64 * vec4f : (v.xy, outerDistance, innerDistance)
65 * v is a normalized vector pointing to the outer edge
66 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
67 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070068 * Additional clip planes are supported for rendering circular arcs. The additional planes are
69 * either intersected or unioned together. Up to three planes are supported (an initial plane,
70 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050071 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070072 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000073 */
74
bsalomoncdaa97b2016-03-08 08:30:14 -080075class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070077 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
78 const SkMatrix& localMatrix)
79 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080080 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070081 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
82 kHigh_GrSLPrecision);
83 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
Jim Van Verth6750e912016-12-19 14:45:19 -050084 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -070086 if (clipPlane) {
87 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
88 } else {
89 fInClipPlane = nullptr;
90 }
91 if (isectPlane) {
92 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
93 } else {
94 fInIsectPlane = nullptr;
95 }
96 if (unionPlane) {
97 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
98 } else {
99 fInUnionPlane = nullptr;
100 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800101 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000102 }
103
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400104 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700105
bsalomoncdaa97b2016-03-08 08:30:14 -0800106 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
mtklein36352bf2015-03-25 18:17:31 -0700108 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
Brian Salomon94efbf52016-11-29 13:43:05 -0500110 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700111 GLSLProcessor::GenKey(*this, caps, b);
112 }
113
Brian Salomon94efbf52016-11-29 13:43:05 -0500114 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700115 return new GLSLProcessor();
116 }
117
118private:
egdaniel57d3b032015-11-13 11:57:27 -0800119 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000120 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800121 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122
Brian Salomon289e3d82016-12-14 15:52:56 -0500123 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800124 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800125 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800126 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800127 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700128 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800129
joshualittabb52a12015-01-13 15:02:10 -0800130 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800131 varyingHandler->emitAttributes(cgp);
Jim Van Verth6750e912016-12-19 14:45:19 -0500132 fragBuilder->codeAppend("highp vec4 circleEdge;");
133 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
134 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 if (cgp.fInClipPlane) {
136 fragBuilder->codeAppend("vec3 clipPlane;");
137 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
138 }
139 if (cgp.fInIsectPlane) {
140 SkASSERT(cgp.fInClipPlane);
141 fragBuilder->codeAppend("vec3 isectPlane;");
142 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
143 }
144 if (cgp.fInUnionPlane) {
145 SkASSERT(cgp.fInClipPlane);
146 fragBuilder->codeAppend("vec3 unionPlane;");
147 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
148 }
joshualittabb52a12015-01-13 15:02:10 -0800149
joshualittb8c241a2015-05-19 08:23:30 -0700150 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700151 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800152
joshualittabb52a12015-01-13 15:02:10 -0800153 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700154 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800155
156 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800157 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800158 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800159 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800160 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700161 cgp.fInPosition->fName,
162 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700163 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800164
Jim Van Verth6750e912016-12-19 14:45:19 -0500165 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700166 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
167 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800168 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500169 fragBuilder->codeAppend(
170 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700171 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800172 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000174
dvonbeck68f2f7d2016-08-01 11:37:45 -0700175 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700176 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
Brian Salomon289e3d82016-12-14 15:52:56 -0500177 fragBuilder->codeAppendf(
178 "if (d == 0.0) {" // if on the center of the circle
179 " %s = vec4(1.0, 0.0, distanceToOuterEdge, "
180 " %s);", // no normalize
181 args.fDistanceVectorName,
182 innerEdgeDistance);
183 fragBuilder->codeAppendf(
184 "} else {"
185 " %s = vec4(normalize(circleEdge.xy),"
186 " distanceToOuterEdge, %s);"
187 "}",
188 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700189 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700190 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500191 fragBuilder->codeAppend(
192 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
193 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700194 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500195 fragBuilder->codeAppend(
196 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
197 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700198 }
199 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500200 fragBuilder->codeAppend(
201 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
202 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700203 }
204 fragBuilder->codeAppend("edgeAlpha *= clip;");
205 }
egdaniel4ca2e602015-11-18 08:01:26 -0800206 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207 }
208
robertphillips46d36f02015-01-18 08:14:14 -0800209 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500210 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700211 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800212 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700215 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500216 key |= cgp.fInClipPlane ? 0x04 : 0x0;
217 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
218 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700219 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220 }
221
bsalomona624bf32016-09-20 09:12:47 -0700222 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
223 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700224 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700225 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700226 }
227
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 private:
egdaniele659a582015-11-13 09:55:43 -0800229 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230 };
231
Brian Salomon289e3d82016-12-14 15:52:56 -0500232 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800233 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800234 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800235 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700236 const Attribute* fInClipPlane;
237 const Attribute* fInIsectPlane;
238 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500239 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000240
joshualittb0a8a372014-09-23 09:50:21 -0700241 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242
joshualitt249af152014-09-15 11:41:13 -0700243 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000244};
245
bsalomoncdaa97b2016-03-08 08:30:14 -0800246GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247
Hal Canary6f6961e2017-01-31 13:50:44 -0500248#if GR_TEST_UTILS
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}
Hal Canary6f6961e2017-01-31 13:50:44 -0500254#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000255
256///////////////////////////////////////////////////////////////////////////////
257
258/**
259 * 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 +0000260 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
261 * in both x and y directions.
262 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000263 * 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 +0000264 */
265
bsalomoncdaa97b2016-03-08 08:30:14 -0800266class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000267public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500268 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800269 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700270 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
271 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
272 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
273 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800274 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000275 }
276
bsalomoncdaa97b2016-03-08 08:30:14 -0800277 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000278
mtklein36352bf2015-03-25 18:17:31 -0700279 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800280
Brian Salomon94efbf52016-11-29 13:43:05 -0500281 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700282 GLSLProcessor::GenKey(*this, caps, b);
283 }
284
Brian Salomon94efbf52016-11-29 13:43:05 -0500285 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700286 return new GLSLProcessor();
287 }
288
289private:
egdaniel57d3b032015-11-13 11:57:27 -0800290 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000291 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800292 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000293
Brian Salomon289e3d82016-12-14 15:52:56 -0500294 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800295 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800296 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800297 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800298 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000299
joshualittabb52a12015-01-13 15:02:10 -0800300 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800301 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800302
egdaniel8dcdedc2015-11-11 06:27:20 -0800303 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800304 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800305 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700306 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000307
egdaniel8dcdedc2015-11-11 06:27:20 -0800308 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800309 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500310 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800311
cdalton85285412016-02-18 12:37:07 -0800312 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700313 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700314 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800315
joshualittabb52a12015-01-13 15:02:10 -0800316 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700317 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800318
319 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800320 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800321 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800322 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800323 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700324 egp.fInPosition->fName,
325 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700326 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800327
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000328 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800329 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
330 ellipseRadii.fsIn());
331 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
332 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
333 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700334
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000335 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800336 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
337 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800338 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000339
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000340 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800341 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500342 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800343 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500344 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
345 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800346 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
347 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000348 }
349
egdaniel4ca2e602015-11-18 08:01:26 -0800350 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000351 }
352
robertphillips46d36f02015-01-18 08:14:14 -0800353 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500354 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700355 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800356 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
357 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700358 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700359 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 }
361
bsalomona624bf32016-09-20 09:12:47 -0700362 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
363 FPCoordTransformIter&& transformIter) override {
364 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
365 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700366 }
367
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000368 private:
egdaniele659a582015-11-13 09:55:43 -0800369 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000370 };
371
joshualitt71c92602015-01-14 08:12:47 -0800372 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800373 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800374 const Attribute* fInEllipseOffset;
375 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700376 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377 bool fStroke;
378
joshualittb0a8a372014-09-23 09:50:21 -0700379 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380
joshualitt249af152014-09-15 11:41:13 -0700381 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000382};
383
bsalomoncdaa97b2016-03-08 08:30:14 -0800384GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000385
Hal Canary6f6961e2017-01-31 13:50:44 -0500386#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700387sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
388 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500389 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000390}
Hal Canary6f6961e2017-01-31 13:50:44 -0500391#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392
393///////////////////////////////////////////////////////////////////////////////
394
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000396 * 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 +0000397 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
398 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
399 * using differentials.
400 *
401 * The result is device-independent and can be used with any affine matrix.
402 */
403
bsalomoncdaa97b2016-03-08 08:30:14 -0800404enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000405
bsalomoncdaa97b2016-03-08 08:30:14 -0800406class DIEllipseGeometryProcessor : public GrGeometryProcessor {
407public:
408 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500409 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800410 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700411 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
412 kHigh_GrSLPrecision);
413 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
414 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
415 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800416 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000417 }
418
bsalomoncdaa97b2016-03-08 08:30:14 -0800419 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000420
mtklein36352bf2015-03-25 18:17:31 -0700421 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000422
Brian Salomon94efbf52016-11-29 13:43:05 -0500423 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700424 GLSLProcessor::GenKey(*this, caps, b);
425 }
halcanary9d524f22016-03-29 09:03:52 -0700426
Brian Salomon94efbf52016-11-29 13:43:05 -0500427 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700428 return new GLSLProcessor();
429 }
430
431private:
egdaniel57d3b032015-11-13 11:57:27 -0800432 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500434 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000435
joshualitt465283c2015-09-11 08:19:35 -0700436 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800437 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800438 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800439 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800440 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000441
joshualittabb52a12015-01-13 15:02:10 -0800442 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800443 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800444
egdaniel8dcdedc2015-11-11 06:27:20 -0800445 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800446 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500447 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700448
egdaniel8dcdedc2015-11-11 06:27:20 -0800449 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800450 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500451 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800452
cdalton85285412016-02-18 12:37:07 -0800453 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700454 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800455
joshualittabb52a12015-01-13 15:02:10 -0800456 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800457 this->setupPosition(vertBuilder,
458 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800459 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700460 diegp.fInPosition->fName,
461 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700462 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800463
464 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800465 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800466 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800467 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800468 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700469 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700470 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800471
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800473 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
474 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
475 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
476 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500477 fragBuilder->codeAppendf(
478 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
479 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
480 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481
egdaniel4ca2e602015-11-18 08:01:26 -0800482 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000483 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800484 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
485 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800486 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800488 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
489 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000490 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800491 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000492 }
493
494 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800495 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800496 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
497 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
498 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
499 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500500 fragBuilder->codeAppendf(
501 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
502 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
503 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
505 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506 }
507
egdaniel4ca2e602015-11-18 08:01:26 -0800508 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000509 }
510
robertphillips46d36f02015-01-18 08:14:14 -0800511 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500512 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700513 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800514 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
515 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700516 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700517 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518 }
519
bsalomona624bf32016-09-20 09:12:47 -0700520 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
521 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800522 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700523
bsalomon31df31c2016-08-17 09:00:24 -0700524 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
525 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700526 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800527 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700528 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
529 }
bsalomona624bf32016-09-20 09:12:47 -0700530 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000531 }
532
533 private:
joshualitt5559ca22015-05-21 15:50:36 -0700534 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700535 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800536
egdaniele659a582015-11-13 09:55:43 -0800537 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000538 };
539
joshualitt71c92602015-01-14 08:12:47 -0800540 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800541 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800542 const Attribute* fInEllipseOffsets0;
543 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500544 SkMatrix fViewMatrix;
545 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546
joshualittb0a8a372014-09-23 09:50:21 -0700547 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548
joshualitt249af152014-09-15 11:41:13 -0700549 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000550};
551
bsalomoncdaa97b2016-03-08 08:30:14 -0800552GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000553
Hal Canary6f6961e2017-01-31 13:50:44 -0500554#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700555sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500556 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
557 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000558}
Hal Canary6f6961e2017-01-31 13:50:44 -0500559#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560
561///////////////////////////////////////////////////////////////////////////////
562
jvanverth6ca48822016-10-07 06:57:32 -0700563// We have two possible cases for geometry for a circle:
564
565// In the case of a normal fill, we draw geometry for the circle as an octagon.
566static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500567 // enter the octagon
568 // clang-format off
569 0, 1, 8, 1, 2, 8,
570 2, 3, 8, 3, 4, 8,
571 4, 5, 8, 5, 6, 8,
572 6, 7, 8, 7, 0, 8
573 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700574};
575
576// For stroked circles, we use two nested octagons.
577static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500578 // enter the octagon
579 // clang-format off
580 0, 1, 9, 0, 9, 8,
581 1, 2, 10, 1, 10, 9,
582 2, 3, 11, 2, 11, 10,
583 3, 4, 12, 3, 12, 11,
584 4, 5, 13, 4, 13, 12,
585 5, 6, 14, 5, 14, 13,
586 6, 7, 15, 6, 15, 14,
587 7, 0, 8, 7, 8, 15,
588 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700589};
590
Brian Salomon289e3d82016-12-14 15:52:56 -0500591
jvanverth6ca48822016-10-07 06:57:32 -0700592static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
593static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
594static const int kVertsPerStrokeCircle = 16;
595static const int kVertsPerFillCircle = 9;
596
597static int circle_type_to_vert_count(bool stroked) {
598 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
599}
600
601static int circle_type_to_index_count(bool stroked) {
602 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
603}
604
605static const uint16_t* circle_type_to_indices(bool stroked) {
606 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
607}
608
609///////////////////////////////////////////////////////////////////////////////
610
Brian Salomon289e3d82016-12-14 15:52:56 -0500611class CircleOp final : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -0800612public:
Brian Salomon25a88092016-12-01 09:36:50 -0500613 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700614
bsalomon4f3a0ca2016-08-22 13:14:26 -0700615 /** Optional extra params to render a partial arc rather than a full circle. */
616 struct ArcParams {
617 SkScalar fStartAngleRadians;
618 SkScalar fSweepAngleRadians;
619 bool fUseCenter;
620 };
Brian Salomon649a3412017-03-09 13:50:43 -0500621 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
622 SkPoint center, SkScalar radius, const GrStyle& style,
623 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700624 SkASSERT(circle_stays_circle(viewMatrix));
625 const SkStrokeRec& stroke = style.strokeRec();
626 if (style.hasPathEffect()) {
627 return nullptr;
628 }
629 SkStrokeRec::Style recStyle = stroke.getStyle();
630 if (arcParams) {
631 // Arc support depends on the style.
632 switch (recStyle) {
633 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500634 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700635 return nullptr;
636 case SkStrokeRec::kFill_Style:
637 // This supports all fills.
638 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500639 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700640 case SkStrokeRec::kHairline_Style:
641 // Strokes that don't use the center point are supported with butt cap.
642 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
643 return nullptr;
644 }
645 break;
646 }
647 }
648
bsalomon4b4a7cc2016-07-08 04:42:54 -0700649 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700650 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700651 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800652
Brian Salomon289e3d82016-12-14 15:52:56 -0500653 bool isStrokeOnly =
654 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700655 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700656
jvanverth6ca48822016-10-07 06:57:32 -0700657 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700658 SkScalar outerRadius = radius;
659 SkScalar halfWidth = 0;
660 if (hasStroke) {
661 if (SkScalarNearlyZero(strokeWidth)) {
662 halfWidth = SK_ScalarHalf;
663 } else {
664 halfWidth = SkScalarHalf(strokeWidth);
665 }
666
667 outerRadius += halfWidth;
668 if (isStrokeOnly) {
669 innerRadius = radius - halfWidth;
670 }
671 }
672
673 // The radii are outset for two reasons. First, it allows the shader to simply perform
674 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
675 // Second, the outer radius is used to compute the verts of the bounding box that is
676 // rendered and the outset ensures the box will cover all partially covered by the circle.
677 outerRadius += SK_ScalarHalf;
678 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700679 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomonf8334782017-01-03 09:42:58 -0500680 std::unique_ptr<CircleOp> op(new CircleOp());
Brian Salomon289e3d82016-12-14 15:52:56 -0500681 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700682
bsalomon4f3a0ca2016-08-22 13:14:26 -0700683 // This makes every point fully inside the intersection plane.
684 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
685 // This makes every point fully outside the union plane.
686 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
687 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
688 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700689 if (arcParams) {
690 // The shader operates in a space where the circle is translated to be centered at the
691 // origin. Here we compute points on the unit circle at the starting and ending angles.
692 SkPoint startPoint, stopPoint;
693 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
694 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
695 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
696 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
697 // radial lines. However, in both cases we have to be careful about the half-circle.
698 // case. In that case the two radial lines are equal and so that edge gets clipped
699 // twice. Since the shared edge goes through the center we fall back on the useCenter
700 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500701 bool useCenter =
702 (arcParams->fUseCenter || isStrokeOnly) &&
703 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700704 if (useCenter) {
705 SkVector norm0 = {startPoint.fY, -startPoint.fX};
706 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
707 if (arcParams->fSweepAngleRadians > 0) {
708 norm0.negate();
709 } else {
710 norm1.negate();
711 }
Brian Salomon289e3d82016-12-14 15:52:56 -0500712 op->fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700713 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500714 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700715 color,
716 innerRadius,
717 outerRadius,
718 {norm0.fX, norm0.fY, 0.5f},
719 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
720 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700721 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500722 stroked});
723 op->fClipPlaneIsect = false;
724 op->fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700725 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500726 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700727 color,
728 innerRadius,
729 outerRadius,
730 {norm0.fX, norm0.fY, 0.5f},
731 {norm1.fX, norm1.fY, 0.5f},
732 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700733 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500734 stroked});
735 op->fClipPlaneIsect = true;
736 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700737 }
738 } else {
739 // We clip to a secant of the original circle.
740 startPoint.scale(radius);
741 stopPoint.scale(radius);
742 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
743 norm.normalize();
744 if (arcParams->fSweepAngleRadians > 0) {
745 norm.negate();
746 }
747 SkScalar d = -norm.dot(startPoint) + 0.5f;
748
Brian Salomon289e3d82016-12-14 15:52:56 -0500749 op->fGeoData.emplace_back(
750 Geometry{color,
751 innerRadius,
752 outerRadius,
753 {norm.fX, norm.fY, d},
754 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
755 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
756 devBounds,
757 stroked});
758 op->fClipPlane = true;
759 op->fClipPlaneIsect = false;
760 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700761 }
762 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500763 op->fGeoData.emplace_back(
764 Geometry{color,
765 innerRadius,
766 outerRadius,
767 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
768 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
769 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
770 devBounds,
771 stroked});
772 op->fClipPlane = false;
773 op->fClipPlaneIsect = false;
774 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700775 }
bsalomon88cf17d2016-07-08 06:40:56 -0700776 // Use the original radius and stroke radius for the bounds so that it does not include the
777 // AA bloat.
778 radius += halfWidth;
Brian Salomon289e3d82016-12-14 15:52:56 -0500779 op->setBounds(
780 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
781 HasAABloat::kYes, IsZeroArea::kNo);
782 op->fVertCount = circle_type_to_vert_count(stroked);
783 op->fIndexCount = circle_type_to_index_count(stroked);
784 op->fAllFill = !stroked;
785 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -0800786 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700787
Brian Salomon289e3d82016-12-14 15:52:56 -0500788 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800789
robertphillipse004bfc2015-11-16 09:06:59 -0800790 SkString dumpInfo() const override {
791 SkString string;
792 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500793 string.appendf(
794 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
795 "InnerRad: %.2f, OuterRad: %.2f\n",
796 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
797 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
798 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800799 }
Brian Salomon7c3e7182016-12-01 09:35:30 -0500800 string.append(DumpPipelineInfo(*this->pipeline()));
robertphillipse004bfc2015-11-16 09:06:59 -0800801 string.append(INHERITED::dumpInfo());
802 return string;
803 }
804
bsalomone46f9fe2015-08-18 06:05:14 -0700805private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500806 CircleOp() : INHERITED(ClassID()) {}
Brian Salomon92aee3d2016-12-21 09:20:25 -0500807
Brian Salomon5298dc82017-02-22 11:52:03 -0500808 void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
809 input->colorInput()->setToConstant(fGeoData[0].fColor);
810 input->coverageInput()->setToUnknown();
Brian Salomon92aee3d2016-12-21 09:20:25 -0500811 }
812
813 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
814 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
815 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800816 fViewMatrixIfUsingLocalCoords.reset();
817 }
joshualitt76e7fb62015-02-11 08:52:27 -0800818 }
819
joshualitt144c3c82015-11-30 12:30:13 -0800820 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800821 SkMatrix localMatrix;
822 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800823 return;
824 }
825
826 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500827 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
828 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700829
830 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500831 SkPoint fPos;
832 GrColor fColor;
833 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700834 SkScalar fOuterRadius;
835 SkScalar fInnerRadius;
836 // These planes may or may not be present in the vertex buffer.
837 SkScalar fHalfPlanes[3][3];
838 };
joshualitt76e7fb62015-02-11 08:52:27 -0800839
joshualitt76e7fb62015-02-11 08:52:27 -0800840 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800841 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500842 SkASSERT(vertexStride ==
843 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
844 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
845 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700846
847 const GrBuffer* vertexBuffer;
848 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500849 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
850 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700851 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700852 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800853 return;
854 }
855
jvanverth6ca48822016-10-07 06:57:32 -0700856 const GrBuffer* indexBuffer = nullptr;
857 int firstIndex = 0;
858 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
859 if (!indices) {
860 SkDebugf("Could not allocate indices\n");
861 return;
862 }
863
864 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800865 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800866 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800867
brianosmanbb2ff942016-02-11 14:15:18 -0800868 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700869 SkScalar innerRadius = geom.fInnerRadius;
870 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800871
bsalomonb5238a72015-05-05 07:49:49 -0700872 const SkRect& bounds = geom.fDevBounds;
Brian Salomon289e3d82016-12-14 15:52:56 -0500873 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
874 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
875 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
876 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
877 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
878 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
879 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
880 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800881
882 // The inner radius in the vertex data must be specified in normalized space.
883 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700884
885 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500886 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700887 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500888
Brian Salomon289e3d82016-12-14 15:52:56 -0500889 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700890 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700891 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700892 v0->fOuterRadius = outerRadius;
893 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800894
Brian Salomon289e3d82016-12-14 15:52:56 -0500895 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700896 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700897 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 v1->fOuterRadius = outerRadius;
899 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800900
Brian Salomon289e3d82016-12-14 15:52:56 -0500901 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700902 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700903 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700904 v2->fOuterRadius = outerRadius;
905 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800906
Brian Salomon289e3d82016-12-14 15:52:56 -0500907 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700908 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700909 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700910 v3->fOuterRadius = outerRadius;
911 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800912
Brian Salomon289e3d82016-12-14 15:52:56 -0500913 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700914 v4->fColor = color;
915 v4->fOffset = SkPoint::Make(octOffset, 1);
916 v4->fOuterRadius = outerRadius;
917 v4->fInnerRadius = innerRadius;
918
Brian Salomon289e3d82016-12-14 15:52:56 -0500919 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700920 v5->fColor = color;
921 v5->fOffset = SkPoint::Make(-octOffset, 1);
922 v5->fOuterRadius = outerRadius;
923 v5->fInnerRadius = innerRadius;
924
Brian Salomon289e3d82016-12-14 15:52:56 -0500925 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700926 v6->fColor = color;
927 v6->fOffset = SkPoint::Make(-1, octOffset);
928 v6->fOuterRadius = outerRadius;
929 v6->fInnerRadius = innerRadius;
930
Brian Salomon289e3d82016-12-14 15:52:56 -0500931 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700932 v7->fColor = color;
933 v7->fOffset = SkPoint::Make(-1, -octOffset);
934 v7->fOuterRadius = outerRadius;
935 v7->fInnerRadius = innerRadius;
936
bsalomon4f3a0ca2016-08-22 13:14:26 -0700937 if (fClipPlane) {
938 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
939 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700942 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
943 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
944 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
945 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700946 }
947 int unionIdx = 1;
948 if (fClipPlaneIsect) {
949 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
950 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700953 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
954 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
955 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
956 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700957 unionIdx = 2;
958 }
959 if (fClipPlaneUnion) {
960 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
961 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700964 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
965 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
966 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
967 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700968 }
jvanverth6ca48822016-10-07 06:57:32 -0700969
970 if (geom.fStroked) {
971 // compute the inner ring
972 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
973 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
974 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
975 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
976 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
977 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
978 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
979 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
980
981 // cosine and sine of pi/8
982 SkScalar c = 0.923579533f;
983 SkScalar s = 0.382683432f;
984 SkScalar r = geom.fInnerRadius;
985
Brian Salomon289e3d82016-12-14 15:52:56 -0500986 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700987 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500988 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700989 v0->fOuterRadius = outerRadius;
990 v0->fInnerRadius = innerRadius;
991
Brian Salomon289e3d82016-12-14 15:52:56 -0500992 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700993 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500994 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700995 v1->fOuterRadius = outerRadius;
996 v1->fInnerRadius = innerRadius;
997
Brian Salomon289e3d82016-12-14 15:52:56 -0500998 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700999 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001000 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001001 v2->fOuterRadius = outerRadius;
1002 v2->fInnerRadius = innerRadius;
1003
Brian Salomon289e3d82016-12-14 15:52:56 -05001004 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001005 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001006 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001007 v3->fOuterRadius = outerRadius;
1008 v3->fInnerRadius = innerRadius;
1009
Brian Salomon289e3d82016-12-14 15:52:56 -05001010 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001011 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001012 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001013 v4->fOuterRadius = outerRadius;
1014 v4->fInnerRadius = innerRadius;
1015
Brian Salomon289e3d82016-12-14 15:52:56 -05001016 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001017 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001018 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001019 v5->fOuterRadius = outerRadius;
1020 v5->fInnerRadius = innerRadius;
1021
Brian Salomon289e3d82016-12-14 15:52:56 -05001022 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001023 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001024 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001025 v6->fOuterRadius = outerRadius;
1026 v6->fInnerRadius = innerRadius;
1027
Brian Salomon289e3d82016-12-14 15:52:56 -05001028 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001029 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001030 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001031 v7->fOuterRadius = outerRadius;
1032 v7->fInnerRadius = innerRadius;
1033
1034 if (fClipPlane) {
1035 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1040 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1041 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1042 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1043 }
1044 int unionIdx = 1;
1045 if (fClipPlaneIsect) {
1046 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1051 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1052 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1053 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1054 unionIdx = 2;
1055 }
1056 if (fClipPlaneUnion) {
1057 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1062 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1063 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1064 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1065 }
1066 } else {
1067 // filled
1068 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1069 v8->fPos = center;
1070 v8->fColor = color;
1071 v8->fOffset = SkPoint::Make(0, 0);
1072 v8->fOuterRadius = outerRadius;
1073 v8->fInnerRadius = innerRadius;
1074 if (fClipPlane) {
1075 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1076 }
1077 int unionIdx = 1;
1078 if (fClipPlaneIsect) {
1079 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1080 unionIdx = 2;
1081 }
1082 if (fClipPlaneUnion) {
1083 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1084 }
1085 }
1086
1087 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1088 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1089 for (int i = 0; i < primIndexCount; ++i) {
1090 *indices++ = primIndices[i] + currStartVertex;
1091 }
1092
1093 currStartVertex += circle_type_to_vert_count(geom.fStroked);
Brian Salomon289e3d82016-12-14 15:52:56 -05001094 vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001095 }
jvanverth6ca48822016-10-07 06:57:32 -07001096
1097 GrMesh mesh;
1098 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1099 firstIndex, fVertCount, fIndexCount);
1100 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001101 }
1102
Brian Salomon25a88092016-12-01 09:36:50 -05001103 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001104 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001105
1106 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001107 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001108 return false;
1109 }
1110
bsalomonabd30f52015-08-13 13:34:48 -07001111 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1112 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001113 return false;
1114 }
1115
jvanverth6ca48822016-10-07 06:57:32 -07001116 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001117 return false;
1118 }
1119
Brian Salomon289e3d82016-12-14 15:52:56 -05001120 // Because we've set up the ops that don't use the planes with noop values
1121 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001122 fClipPlane |= that->fClipPlane;
1123 fClipPlaneIsect |= that->fClipPlaneIsect;
1124 fClipPlaneUnion |= that->fClipPlaneUnion;
1125
bsalomoncdaa97b2016-03-08 08:30:14 -08001126 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001127 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001128 fVertCount += that->fVertCount;
1129 fIndexCount += that->fIndexCount;
1130 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001131 return true;
1132 }
1133
bsalomon4b4a7cc2016-07-08 04:42:54 -07001134 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05001135 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001136 SkScalar fInnerRadius;
1137 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001138 SkScalar fClipPlane[3];
1139 SkScalar fIsectPlane[3];
1140 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001141 SkRect fDevBounds;
1142 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001143 };
1144
jvanverth6ca48822016-10-07 06:57:32 -07001145 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05001146 SkMatrix fViewMatrixIfUsingLocalCoords;
1147 int fVertCount;
1148 int fIndexCount;
1149 bool fAllFill;
1150 bool fClipPlane;
1151 bool fClipPlaneIsect;
1152 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001153
Brian Salomondad29232016-12-01 16:40:24 -05001154 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001155};
1156
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001157///////////////////////////////////////////////////////////////////////////////
1158
Brian Salomon289e3d82016-12-14 15:52:56 -05001159class EllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001160public:
Brian Salomon25a88092016-12-01 09:36:50 -05001161 DEFINE_OP_CLASS_ID
Brian Salomon649a3412017-03-09 13:50:43 -05001162 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
1163 const SkRect& ellipse, const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001164 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001165
bsalomon4b4a7cc2016-07-08 04:42:54 -07001166 // do any matrix crunching before we reset the draw state for device coords
1167 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1168 viewMatrix.mapPoints(&center, 1);
1169 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1170 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon289e3d82016-12-14 15:52:56 -05001171 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1172 viewMatrix[SkMatrix::kMSkewY] * ellipseYRadius);
1173 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * ellipseXRadius +
1174 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001175
bsalomon4b4a7cc2016-07-08 04:42:54 -07001176 // do (potentially) anisotropic mapping of stroke
1177 SkVector scaledStroke;
1178 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001179 scaledStroke.fX = SkScalarAbs(
1180 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1181 scaledStroke.fY = SkScalarAbs(
1182 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001183
1184 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001185 bool isStrokeOnly =
1186 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001187 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1188
1189 SkScalar innerXRadius = 0;
1190 SkScalar innerYRadius = 0;
1191 if (hasStroke) {
1192 if (SkScalarNearlyZero(scaledStroke.length())) {
1193 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1194 } else {
1195 scaledStroke.scale(SK_ScalarHalf);
1196 }
1197
1198 // we only handle thick strokes for near-circular ellipses
1199 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001200 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001201 return nullptr;
1202 }
1203
1204 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001205 if (scaledStroke.fX * (yRadius * yRadius) <
1206 (scaledStroke.fY * scaledStroke.fY) * xRadius ||
1207 scaledStroke.fY * (xRadius * xRadius) <
1208 (scaledStroke.fX * scaledStroke.fX) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001209 return nullptr;
1210 }
1211
1212 // this is legit only if scale & translation (which should be the case at the moment)
1213 if (isStrokeOnly) {
1214 innerXRadius = xRadius - scaledStroke.fX;
1215 innerYRadius = yRadius - scaledStroke.fY;
1216 }
1217
1218 xRadius += scaledStroke.fX;
1219 yRadius += scaledStroke.fY;
1220 }
1221
Brian Salomonf8334782017-01-03 09:42:58 -05001222 std::unique_ptr<EllipseOp> op(new EllipseOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05001223 op->fGeoData.emplace_back(
1224 Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
1225 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1226 center.fX + xRadius, center.fY + yRadius)});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001227
Brian Salomon289e3d82016-12-14 15:52:56 -05001228 op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001229
bsalomon4b4a7cc2016-07-08 04:42:54 -07001230 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon289e3d82016-12-14 15:52:56 -05001231 op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001232
Brian Salomon289e3d82016-12-14 15:52:56 -05001233 op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1234 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
1235 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -08001236 }
joshualitt76e7fb62015-02-11 08:52:27 -08001237
Brian Salomon289e3d82016-12-14 15:52:56 -05001238 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001239
Brian Salomon7c3e7182016-12-01 09:35:30 -05001240 SkString dumpInfo() const override {
1241 SkString string;
1242 string.appendf("Stroked: %d\n", fStroked);
1243 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001244 string.appendf(
1245 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1246 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1247 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1248 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1249 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001250 }
1251 string.append(DumpPipelineInfo(*this->pipeline()));
1252 string.append(INHERITED::dumpInfo());
1253 return string;
1254 }
1255
bsalomone46f9fe2015-08-18 06:05:14 -07001256private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001257 EllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001258
Brian Salomon5298dc82017-02-22 11:52:03 -05001259 void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
1260 input->colorInput()->setToConstant(fGeoData[0].fColor);
1261 input->coverageInput()->setToUnknown();
Brian Salomon92aee3d2016-12-21 09:20:25 -05001262 }
1263
1264 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -05001265 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001266 fViewMatrixIfUsingLocalCoords.reset();
1267 }
joshualitt76e7fb62015-02-11 08:52:27 -08001268 }
1269
joshualitt144c3c82015-11-30 12:30:13 -08001270 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001271 SkMatrix localMatrix;
1272 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001273 return;
1274 }
1275
1276 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001277 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001278
joshualitt76e7fb62015-02-11 08:52:27 -08001279 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001280 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001281 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001282 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon289e3d82016-12-14 15:52:56 -05001283 EllipseVertex* verts =
1284 reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001285 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001286 return;
1287 }
1288
bsalomon8415abe2015-05-04 11:41:41 -07001289 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001290 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001291
brianosmanbb2ff942016-02-11 14:15:18 -08001292 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001293 SkScalar xRadius = geom.fXRadius;
1294 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001295
1296 // Compute the reciprocals of the radii here to save time in the shader
1297 SkScalar xRadRecip = SkScalarInvert(xRadius);
1298 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001299 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1300 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001301
bsalomonb5238a72015-05-05 07:49:49 -07001302 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001303
vjiaoblack977996d2016-06-30 12:20:54 -07001304 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1305 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1306 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1307
joshualitt76e7fb62015-02-11 08:52:27 -08001308 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon289e3d82016-12-14 15:52:56 -05001309 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001310 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001311 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001312 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1313 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1314
Brian Salomon289e3d82016-12-14 15:52:56 -05001315 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001316 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001317 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001318 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1319 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1320
1321 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001322 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001323 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001324 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1325 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1326
1327 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001328 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001329 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001330 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1331 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1332
bsalomonb5238a72015-05-05 07:49:49 -07001333 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001334 }
Hal Canary144caf52016-11-07 17:57:18 -05001335 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001336 }
1337
Brian Salomon25a88092016-12-01 09:36:50 -05001338 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001339 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001340
1341 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1342 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001343 return false;
1344 }
1345
bsalomoncdaa97b2016-03-08 08:30:14 -08001346 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001347 return false;
1348 }
1349
bsalomoncdaa97b2016-03-08 08:30:14 -08001350 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001351 return false;
1352 }
1353
bsalomoncdaa97b2016-03-08 08:30:14 -08001354 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001355 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001356 return true;
1357 }
1358
bsalomon4b4a7cc2016-07-08 04:42:54 -07001359 struct Geometry {
1360 GrColor fColor;
1361 SkScalar fXRadius;
1362 SkScalar fYRadius;
1363 SkScalar fInnerXRadius;
1364 SkScalar fInnerYRadius;
1365 SkRect fDevBounds;
1366 };
joshualitt76e7fb62015-02-11 08:52:27 -08001367
Brian Salomon289e3d82016-12-14 15:52:56 -05001368 bool fStroked;
1369 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001370 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001371
Brian Salomondad29232016-12-01 16:40:24 -05001372 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001373};
1374
joshualitt76e7fb62015-02-11 08:52:27 -08001375/////////////////////////////////////////////////////////////////////////////////////////////////
1376
Brian Salomon289e3d82016-12-14 15:52:56 -05001377class DIEllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001378public:
Brian Salomon25a88092016-12-01 09:36:50 -05001379 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001380
Brian Salomon649a3412017-03-09 13:50:43 -05001381 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
1382 const SkMatrix& viewMatrix,
1383 const SkRect& ellipse,
1384 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001385 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1386 SkScalar xRadius = SkScalarHalf(ellipse.width());
1387 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001388
bsalomon4b4a7cc2016-07-08 04:42:54 -07001389 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001390 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
1391 ? DIEllipseStyle::kStroke
1392 : (SkStrokeRec::kHairline_Style == style)
1393 ? DIEllipseStyle::kHairline
1394 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001395
1396 SkScalar innerXRadius = 0;
1397 SkScalar innerYRadius = 0;
1398 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1399 SkScalar strokeWidth = stroke.getWidth();
1400
1401 if (SkScalarNearlyZero(strokeWidth)) {
1402 strokeWidth = SK_ScalarHalf;
1403 } else {
1404 strokeWidth *= SK_ScalarHalf;
1405 }
1406
1407 // we only handle thick strokes for near-circular ellipses
1408 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001409 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001410 return nullptr;
1411 }
1412
1413 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001414 if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
1415 strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001416 return nullptr;
1417 }
1418
1419 // set inner radius (if needed)
1420 if (SkStrokeRec::kStroke_Style == style) {
1421 innerXRadius = xRadius - strokeWidth;
1422 innerYRadius = yRadius - strokeWidth;
1423 }
1424
1425 xRadius += strokeWidth;
1426 yRadius += strokeWidth;
1427 }
1428 if (DIEllipseStyle::kStroke == dieStyle) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001429 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
1430 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001431 }
1432
1433 // This expands the outer rect so that after CTM we end up with a half-pixel border
1434 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1435 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1436 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1437 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001438 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1439 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001440
Brian Salomonf8334782017-01-03 09:42:58 -05001441 std::unique_ptr<DIEllipseOp> op(new DIEllipseOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05001442 op->fGeoData.emplace_back(Geometry{
1443 viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
1444 dieStyle,
1445 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1446 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
1447 op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1448 IsZeroArea::kNo);
1449 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08001450 }
1451
Brian Salomon289e3d82016-12-14 15:52:56 -05001452 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001453
Brian Salomon7c3e7182016-12-01 09:35:30 -05001454 SkString dumpInfo() const override {
1455 SkString string;
1456 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001457 string.appendf(
1458 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1459 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1460 "GeoDY: %.2f\n",
1461 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1462 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1463 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001464 }
1465 string.append(DumpPipelineInfo(*this->pipeline()));
1466 string.append(INHERITED::dumpInfo());
1467 return string;
1468 }
1469
bsalomone46f9fe2015-08-18 06:05:14 -07001470private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001471 DIEllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001472
Brian Salomon5298dc82017-02-22 11:52:03 -05001473 void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
1474 input->colorInput()->setToConstant(fGeoData[0].fColor);
1475 input->coverageInput()->setToUnknown();
Brian Salomon92aee3d2016-12-21 09:20:25 -05001476 }
1477
1478 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1479 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1480 fUsesLocalCoords = optimizations.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001481 }
1482
joshualitt144c3c82015-11-30 12:30:13 -08001483 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001484 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001485 sk_sp<GrGeometryProcessor> gp(
1486 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001487
joshualitt76e7fb62015-02-11 08:52:27 -08001488 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001489 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001490 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001491 QuadHelper helper;
1492 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05001493 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001494 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001495 return;
1496 }
1497
joshualitt76e7fb62015-02-11 08:52:27 -08001498 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001499 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001500
brianosmanbb2ff942016-02-11 14:15:18 -08001501 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001502 SkScalar xRadius = geom.fXRadius;
1503 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001504
bsalomonb5238a72015-05-05 07:49:49 -07001505 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001506
1507 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001508 SkScalar offsetDx = geom.fGeoDx / xRadius;
1509 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001510
reed80ea19c2015-05-12 10:37:34 -07001511 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1512 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001513
1514 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001515 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001516 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1517 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1518
Brian Salomon289e3d82016-12-14 15:52:56 -05001519 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001520 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001521 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1522 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1523
1524 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001525 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001526 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1527 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1528
1529 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001530 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001531 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1532 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1533
bsalomonb5238a72015-05-05 07:49:49 -07001534 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001535 }
Hal Canary144caf52016-11-07 17:57:18 -05001536 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001537 }
halcanary9d524f22016-03-29 09:03:52 -07001538
Brian Salomon25a88092016-12-01 09:36:50 -05001539 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001540 DIEllipseOp* that = t->cast<DIEllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001541 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1542 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001543 return false;
1544 }
1545
bsalomoncdaa97b2016-03-08 08:30:14 -08001546 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001547 return false;
1548 }
1549
joshualittd96a67b2015-05-05 14:09:05 -07001550 // TODO rewrite to allow positioning on CPU
1551 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001552 return false;
1553 }
1554
bsalomoncdaa97b2016-03-08 08:30:14 -08001555 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001556 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001557 return true;
1558 }
1559
joshualitt76e7fb62015-02-11 08:52:27 -08001560 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001561 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001562
bsalomon4b4a7cc2016-07-08 04:42:54 -07001563 struct Geometry {
1564 SkMatrix fViewMatrix;
1565 GrColor fColor;
1566 SkScalar fXRadius;
1567 SkScalar fYRadius;
1568 SkScalar fInnerXRadius;
1569 SkScalar fInnerYRadius;
1570 SkScalar fGeoDx;
1571 SkScalar fGeoDy;
1572 DIEllipseStyle fStyle;
1573 SkRect fBounds;
1574 };
1575
Brian Salomon289e3d82016-12-14 15:52:56 -05001576 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001577 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001578
Brian Salomondad29232016-12-01 16:40:24 -05001579 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001580};
1581
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001582///////////////////////////////////////////////////////////////////////////////
1583
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001584// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001585//
1586// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1587// ____________
1588// |_|________|_|
1589// | | | |
1590// | | | |
1591// | | | |
1592// |_|________|_|
1593// |_|________|_|
1594//
1595// For strokes, we don't draw the center quad.
1596//
1597// For circular roundrects, in the case where the stroke width is greater than twice
1598// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001599// in the center. The shared vertices are duplicated so we can set a different outer radius
1600// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001601// ____________
1602// |_|________|_|
1603// | |\ ____ /| |
1604// | | | | | |
1605// | | |____| | |
1606// |_|/______\|_|
1607// |_|________|_|
1608//
1609// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001610//
1611// For filled rrects that need to provide a distance vector we resuse the overstroke
1612// geometry but make the inner rect degenerate (either a point or a horizontal or
1613// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001614
jvanverth84839f62016-08-29 10:16:40 -07001615static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001616 // clang-format off
1617 // overstroke quads
1618 // we place this at the beginning so that we can skip these indices when rendering normally
1619 16, 17, 19, 16, 19, 18,
1620 19, 17, 23, 19, 23, 21,
1621 21, 23, 22, 21, 22, 20,
1622 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001623
Brian Salomon289e3d82016-12-14 15:52:56 -05001624 // corners
1625 0, 1, 5, 0, 5, 4,
1626 2, 3, 7, 2, 7, 6,
1627 8, 9, 13, 8, 13, 12,
1628 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001629
Brian Salomon289e3d82016-12-14 15:52:56 -05001630 // edges
1631 1, 2, 6, 1, 6, 5,
1632 4, 5, 9, 4, 9, 8,
1633 6, 7, 11, 6, 11, 10,
1634 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001635
Brian Salomon289e3d82016-12-14 15:52:56 -05001636 // center
1637 // we place this at the end so that we can ignore these indices when not rendering as filled
1638 5, 6, 10, 5, 10, 9,
1639 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001640};
Brian Salomon289e3d82016-12-14 15:52:56 -05001641
jvanverth84839f62016-08-29 10:16:40 -07001642// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001643static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001644
jvanverth84839f62016-08-29 10:16:40 -07001645// overstroke count is arraysize minus the center indices
1646static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1647// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001648static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001649// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001650static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1651static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001652static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001653
jvanverthc3d0e422016-08-25 08:12:35 -07001654enum RRectType {
1655 kFill_RRectType,
1656 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001657 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001658 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001659};
1660
jvanverth84839f62016-08-29 10:16:40 -07001661static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001662 switch (type) {
1663 case kFill_RRectType:
1664 case kStroke_RRectType:
1665 return kVertsPerStandardRRect;
1666 case kOverstroke_RRectType:
1667 case kFillWithDist_RRectType:
1668 return kVertsPerOverstrokeRRect;
1669 }
1670 SkFAIL("Invalid type");
1671 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001672}
1673
1674static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001675 switch (type) {
1676 case kFill_RRectType:
1677 return kIndicesPerFillRRect;
1678 case kStroke_RRectType:
1679 return kIndicesPerStrokeRRect;
1680 case kOverstroke_RRectType:
1681 case kFillWithDist_RRectType:
1682 return kIndicesPerOverstrokeRRect;
1683 }
1684 SkFAIL("Invalid type");
1685 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001686}
1687
1688static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001689 switch (type) {
1690 case kFill_RRectType:
1691 case kStroke_RRectType:
1692 return gStandardRRectIndices;
1693 case kOverstroke_RRectType:
1694 case kFillWithDist_RRectType:
1695 return gOverstrokeRRectIndices;
1696 }
1697 SkFAIL("Invalid type");
1698 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001699}
1700
joshualitt76e7fb62015-02-11 08:52:27 -08001701///////////////////////////////////////////////////////////////////////////////////////////////////
1702
Robert Phillips79839d42016-10-06 15:03:34 -04001703// For distance computations in the interior of filled rrects we:
1704//
1705// add a interior degenerate (point or line) rect
1706// each vertex of that rect gets -outerRad as its radius
1707// this makes the computation of the distance to the outer edge be negative
1708// negative values are caught and then handled differently in the GP's onEmitCode
1709// each vertex is also given the normalized x & y distance from the interior rect's edge
1710// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1711
Brian Salomon289e3d82016-12-14 15:52:56 -05001712class CircularRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001713public:
Brian Salomon25a88092016-12-01 09:36:50 -05001714 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001715
bsalomon4b4a7cc2016-07-08 04:42:54 -07001716 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1717 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05001718 CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1719 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1720 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001721 SkRect bounds = devRect;
1722 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1723 SkScalar innerRadius = 0.0f;
1724 SkScalar outerRadius = devRadius;
1725 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001726 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001727 if (devStrokeWidth > 0) {
1728 if (SkScalarNearlyZero(devStrokeWidth)) {
1729 halfWidth = SK_ScalarHalf;
1730 } else {
1731 halfWidth = SkScalarHalf(devStrokeWidth);
1732 }
joshualitt76e7fb62015-02-11 08:52:27 -08001733
bsalomon4b4a7cc2016-07-08 04:42:54 -07001734 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001735 // Outset stroke by 1/4 pixel
1736 devStrokeWidth += 0.25f;
1737 // If stroke is greater than width or height, this is still a fill
1738 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001739 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001740 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001741 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001742 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001743 }
1744 outerRadius += halfWidth;
1745 bounds.outset(halfWidth, halfWidth);
1746 }
Robert Phillips79839d42016-10-06 15:03:34 -04001747 if (kFill_RRectType == type && needsDistance) {
1748 type = kFillWithDist_RRectType;
1749 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001750
bsalomon4b4a7cc2016-07-08 04:42:54 -07001751 // The radii are outset for two reasons. First, it allows the shader to simply perform
1752 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1753 // Second, the outer radius is used to compute the verts of the bounding box that is
1754 // rendered and the outset ensures the box will cover all partially covered by the rrect
1755 // corners.
1756 outerRadius += SK_ScalarHalf;
1757 innerRadius -= SK_ScalarHalf;
1758
bsalomon88cf17d2016-07-08 06:40:56 -07001759 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1760
1761 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001762 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1763
Brian Salomon289e3d82016-12-14 15:52:56 -05001764 fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001765 fVertCount = rrect_type_to_vert_count(type);
1766 fIndexCount = rrect_type_to_index_count(type);
1767 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001768 }
1769
Brian Salomon289e3d82016-12-14 15:52:56 -05001770 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001771
jvanverthc3d0e422016-08-25 08:12:35 -07001772 SkString dumpInfo() const override {
1773 SkString string;
1774 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001775 string.appendf(
1776 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1777 "InnerRad: %.2f, OuterRad: %.2f\n",
1778 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1779 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1780 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001781 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001782 string.append(DumpPipelineInfo(*this->pipeline()));
jvanverthc3d0e422016-08-25 08:12:35 -07001783 string.append(INHERITED::dumpInfo());
1784 return string;
1785 }
1786
Brian Salomon92aee3d2016-12-21 09:20:25 -05001787private:
Brian Salomon5298dc82017-02-22 11:52:03 -05001788 void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
1789 input->colorInput()->setToConstant(fGeoData[0].fColor);
1790 input->coverageInput()->setToUnknown();
joshualitt76e7fb62015-02-11 08:52:27 -08001791 }
1792
Brian Salomon92aee3d2016-12-21 09:20:25 -05001793 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1794 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1795 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001796 fViewMatrixIfUsingLocalCoords.reset();
1797 }
joshualitt76e7fb62015-02-11 08:52:27 -08001798 }
1799
Robert Phillips79839d42016-10-06 15:03:34 -04001800 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001801 SkPoint fPos;
1802 GrColor fColor;
1803 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001804 SkScalar fOuterRadius;
1805 SkScalar fInnerRadius;
1806 // No half plane, we don't use it here.
1807 };
1808
Brian Salomon289e3d82016-12-14 15:52:56 -05001809 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1810 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1811 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001812 SkASSERT(smInset < bigInset);
1813
1814 // TL
1815 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1816 (*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
1822 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001823 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001824 (*verts)->fColor = color;
1825 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1826 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001827 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001828 (*verts)++;
1829
Brian Salomon289e3d82016-12-14 15:52:56 -05001830 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001831 (*verts)->fColor = color;
1832 (*verts)->fOffset = SkPoint::Make(0, 0);
1833 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001834 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001835 (*verts)++;
1836
Brian Salomon289e3d82016-12-14 15:52:56 -05001837 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001838 (*verts)->fColor = color;
1839 (*verts)->fOffset = SkPoint::Make(0, 0);
1840 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001841 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001842 (*verts)++;
1843
1844 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1845 (*verts)->fColor = color;
1846 (*verts)->fOffset = SkPoint::Make(0, 0);
1847 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001848 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001849 (*verts)++;
1850
1851 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1852 (*verts)->fColor = color;
1853 (*verts)->fOffset = SkPoint::Make(0, 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 // BL
1859 (*verts)->fPos = SkPoint::Make(bounds.fLeft + 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 // BR
1867 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1868 (*verts)->fColor = color;
1869 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1870 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001871 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001872 (*verts)++;
1873 }
1874
joshualitt144c3c82015-11-30 12:30:13 -08001875 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001876 // Invert the view matrix as a local matrix (if any other processors require coords).
1877 SkMatrix localMatrix;
1878 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001879 return;
1880 }
1881
1882 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001883 sk_sp<GrGeometryProcessor> gp(
1884 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001885
joshualitt76e7fb62015-02-11 08:52:27 -08001886 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001887 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001888 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001889
jvanverth84839f62016-08-29 10:16:40 -07001890 const GrBuffer* vertexBuffer;
1891 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001892
Brian Salomon289e3d82016-12-14 15:52:56 -05001893 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1894 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001895 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001896 SkDebugf("Could not allocate vertices\n");
1897 return;
1898 }
1899
jvanverth84839f62016-08-29 10:16:40 -07001900 const GrBuffer* indexBuffer = nullptr;
1901 int firstIndex = 0;
1902 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1903 if (!indices) {
1904 SkDebugf("Could not allocate indices\n");
1905 return;
1906 }
1907
1908 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001909 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001910 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001911
brianosmanbb2ff942016-02-11 14:15:18 -08001912 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001913 SkScalar outerRadius = args.fOuterRadius;
1914
egdanielbc227142015-04-21 06:28:08 -07001915 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001916
Brian Salomon289e3d82016-12-14 15:52:56 -05001917 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1918 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001919
Brian Salomon289e3d82016-12-14 15:52:56 -05001920 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001921 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001922 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomon289e3d82016-12-14 15:52:56 -05001923 SkScalar innerRadius =
1924 args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
1925 ? args.fInnerRadius / args.fOuterRadius
1926 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001927 for (int i = 0; i < 4; ++i) {
1928 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001929 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001930 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1931 verts->fOuterRadius = outerRadius;
1932 verts->fInnerRadius = innerRadius;
1933 verts++;
1934
1935 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001936 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001937 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1938 verts->fOuterRadius = outerRadius;
1939 verts->fInnerRadius = innerRadius;
1940 verts++;
1941
1942 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001943 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001944 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1945 verts->fOuterRadius = outerRadius;
1946 verts->fInnerRadius = innerRadius;
1947 verts++;
1948
1949 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001950 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001951 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1952 verts->fOuterRadius = outerRadius;
1953 verts->fInnerRadius = innerRadius;
1954 verts++;
1955 }
jvanverthc3d0e422016-08-25 08:12:35 -07001956 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001957 // Effectively this is an additional stroked rrect, with its
1958 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1959 // This will give us correct AA in the center and the correct
1960 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001961 //
jvanvertha4f1af82016-08-29 07:17:47 -07001962 // Also, the outer offset is a constant vector pointing to the right, which
1963 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001964 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001965 SkASSERT(args.fInnerRadius <= 0.0f);
1966
jvanvertha4f1af82016-08-29 07:17:47 -07001967 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1968 // this is the normalized distance from the outer rectangle of this
1969 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001970 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001971
Brian Salomon289e3d82016-12-14 15:52:56 -05001972 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1973 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001974 }
jvanverth6a397612016-08-26 08:15:33 -07001975
Robert Phillips79839d42016-10-06 15:03:34 -04001976 if (kFillWithDist_RRectType == args.fType) {
1977 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001978
Robert Phillips79839d42016-10-06 15:03:34 -04001979 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001980
Brian Salomon289e3d82016-12-14 15:52:56 -05001981 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
1982 -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001983 }
jvanverth84839f62016-08-29 10:16:40 -07001984
1985 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1986 const int primIndexCount = rrect_type_to_index_count(args.fType);
1987 for (int i = 0; i < primIndexCount; ++i) {
1988 *indices++ = primIndices[i] + currStartVertex;
1989 }
1990
1991 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001992 }
1993
jvanverth84839f62016-08-29 10:16:40 -07001994 GrMesh mesh;
1995 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1996 firstIndex, fVertCount, fIndexCount);
1997 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001998 }
1999
Brian Salomon25a88092016-12-01 09:36:50 -05002000 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002001 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002002
2003 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002004 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002005 return false;
2006 }
2007
bsalomonabd30f52015-08-13 13:34:48 -07002008 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2009 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002010 return false;
2011 }
2012
bsalomoncdaa97b2016-03-08 08:30:14 -08002013 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002014 return false;
2015 }
2016
bsalomoncdaa97b2016-03-08 08:30:14 -08002017 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002018 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002019 fVertCount += that->fVertCount;
2020 fIndexCount += that->fIndexCount;
2021 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002022 return true;
2023 }
2024
bsalomon4b4a7cc2016-07-08 04:42:54 -07002025 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05002026 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002027 SkScalar fInnerRadius;
2028 SkScalar fOuterRadius;
2029 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002030 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002031 };
2032
joshualitt76e7fb62015-02-11 08:52:27 -08002033 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05002034 SkMatrix fViewMatrixIfUsingLocalCoords;
2035 int fVertCount;
2036 int fIndexCount;
2037 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002038
Brian Salomondad29232016-12-01 16:40:24 -05002039 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002040};
2041
jvanverth84839f62016-08-29 10:16:40 -07002042static const int kNumRRectsInIndexBuffer = 256;
2043
2044GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2045GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2046static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2047 GrResourceProvider* resourceProvider) {
2048 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2049 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2050 switch (type) {
2051 case kFill_RRectType:
2052 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002053 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2054 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002055 case kStroke_RRectType:
2056 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002057 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2058 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002059 default:
2060 SkASSERT(false);
2061 return nullptr;
2062 };
2063}
2064
Brian Salomon289e3d82016-12-14 15:52:56 -05002065class EllipticalRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08002066public:
Brian Salomon25a88092016-12-01 09:36:50 -05002067 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002068
bsalomon4b4a7cc2016-07-08 04:42:54 -07002069 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2070 // whether the rrect is only stroked or stroked and filled.
Brian Salomon649a3412017-03-09 13:50:43 -05002071 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
2072 const SkRect& devRect, float devXRadius,
2073 float devYRadius, SkVector devStrokeWidths,
2074 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002075 SkASSERT(devXRadius > 0.5);
2076 SkASSERT(devYRadius > 0.5);
2077 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2078 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2079 SkScalar innerXRadius = 0.0f;
2080 SkScalar innerYRadius = 0.0f;
2081 SkRect bounds = devRect;
2082 bool stroked = false;
2083 if (devStrokeWidths.fX > 0) {
2084 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2085 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2086 } else {
2087 devStrokeWidths.scale(SK_ScalarHalf);
2088 }
joshualitt76e7fb62015-02-11 08:52:27 -08002089
bsalomon4b4a7cc2016-07-08 04:42:54 -07002090 // we only handle thick strokes for near-circular ellipses
2091 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002092 (SK_ScalarHalf * devXRadius > devYRadius ||
2093 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002094 return nullptr;
2095 }
2096
2097 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002098 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2099 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002100 return nullptr;
2101 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002102 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2103 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002104 return nullptr;
2105 }
2106
2107 // this is legit only if scale & translation (which should be the case at the moment)
2108 if (strokeOnly) {
2109 innerXRadius = devXRadius - devStrokeWidths.fX;
2110 innerYRadius = devYRadius - devStrokeWidths.fY;
2111 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2112 }
2113
2114 devXRadius += devStrokeWidths.fX;
2115 devYRadius += devStrokeWidths.fY;
2116 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2117 }
2118
Brian Salomonf8334782017-01-03 09:42:58 -05002119 std::unique_ptr<EllipticalRRectOp> op(new EllipticalRRectOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05002120 op->fStroked = stroked;
2121 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
2122 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002123 // Expand the rect for aa in order to generate the correct vertices.
2124 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon289e3d82016-12-14 15:52:56 -05002125 op->fGeoData.emplace_back(
2126 Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2127 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08002128 }
2129
Brian Salomon289e3d82016-12-14 15:52:56 -05002130 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002131
Brian Salomon7c3e7182016-12-01 09:35:30 -05002132 SkString dumpInfo() const override {
2133 SkString string;
2134 string.appendf("Stroked: %d\n", fStroked);
2135 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002136 string.appendf(
2137 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2138 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2139 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2140 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2141 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002142 }
2143 string.append(DumpPipelineInfo(*this->pipeline()));
2144 string.append(INHERITED::dumpInfo());
2145 return string;
2146 }
2147
bsalomone46f9fe2015-08-18 06:05:14 -07002148private:
Brian Salomon289e3d82016-12-14 15:52:56 -05002149 EllipticalRRectOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07002150
Brian Salomon5298dc82017-02-22 11:52:03 -05002151 void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
2152 input->colorInput()->setToConstant(fGeoData[0].fColor);
2153 input->coverageInput()->setToUnknown();
Brian Salomon92aee3d2016-12-21 09:20:25 -05002154 }
2155
2156 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
2157 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
2158 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08002159 fViewMatrixIfUsingLocalCoords.reset();
2160 }
joshualitt76e7fb62015-02-11 08:52:27 -08002161 }
2162
joshualitt144c3c82015-11-30 12:30:13 -08002163 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002164 SkMatrix localMatrix;
2165 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002166 return;
2167 }
2168
2169 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002170 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002171
joshualitt76e7fb62015-02-11 08:52:27 -08002172 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002173 size_t vertexStride = gp->getVertexStride();
2174 SkASSERT(vertexStride == sizeof(EllipseVertex));
2175
bsalomonb5238a72015-05-05 07:49:49 -07002176 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002177 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002178 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2179 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002180
bsalomonb5238a72015-05-05 07:49:49 -07002181 InstancedHelper helper;
2182 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05002183 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
2184 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002185 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002186 SkDebugf("Could not allocate vertices\n");
2187 return;
2188 }
2189
joshualitt76e7fb62015-02-11 08:52:27 -08002190 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002191 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002192
brianosmanbb2ff942016-02-11 14:15:18 -08002193 GrColor color = args.fColor;
2194
joshualitt76e7fb62015-02-11 08:52:27 -08002195 // Compute the reciprocals of the radii here to save time in the shader
2196 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2197 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2198 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2199 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2200
2201 // Extend the radii out half a pixel to antialias.
2202 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2203 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2204
egdanielbc227142015-04-21 06:28:08 -07002205 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002206
Brian Salomon289e3d82016-12-14 15:52:56 -05002207 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2208 bounds.fBottom - yOuterRadius, bounds.fBottom};
2209 SkScalar yOuterOffsets[4] = {yOuterRadius,
2210 SK_ScalarNearlyZero, // we're using inversesqrt() in
2211 // shader, so can't be exactly 0
2212 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002213
2214 for (int i = 0; i < 4; ++i) {
2215 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002216 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002217 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2218 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2219 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2220 verts++;
2221
2222 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002223 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002224 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2225 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2226 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2227 verts++;
2228
2229 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002230 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002231 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2232 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2233 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2234 verts++;
2235
2236 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002237 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002238 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2239 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2240 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2241 verts++;
2242 }
2243 }
Hal Canary144caf52016-11-07 17:57:18 -05002244 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08002245 }
2246
Brian Salomon25a88092016-12-01 09:36:50 -05002247 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002248 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002249
2250 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2251 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002252 return false;
2253 }
2254
bsalomoncdaa97b2016-03-08 08:30:14 -08002255 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002256 return false;
2257 }
2258
bsalomoncdaa97b2016-03-08 08:30:14 -08002259 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002260 return false;
2261 }
2262
bsalomoncdaa97b2016-03-08 08:30:14 -08002263 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002264 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002265 return true;
2266 }
2267
bsalomon4b4a7cc2016-07-08 04:42:54 -07002268 struct Geometry {
2269 GrColor fColor;
2270 SkScalar fXRadius;
2271 SkScalar fYRadius;
2272 SkScalar fInnerXRadius;
2273 SkScalar fInnerYRadius;
2274 SkRect fDevBounds;
2275 };
2276
Brian Salomon289e3d82016-12-14 15:52:56 -05002277 bool fStroked;
2278 SkMatrix fViewMatrixIfUsingLocalCoords;
2279 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002280
Brian Salomondad29232016-12-01 16:40:24 -05002281 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002282};
2283
Brian Salomon649a3412017-03-09 13:50:43 -05002284static std::unique_ptr<GrMeshDrawOp> make_rrect_op(GrColor color,
2285 bool needsDistance,
2286 const SkMatrix& viewMatrix,
2287 const SkRRect& rrect,
2288 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002289 SkASSERT(viewMatrix.rectStaysRect());
2290 SkASSERT(rrect.isSimple());
2291 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002292
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002293 // RRect ops only handle simple, but not too simple, rrects.
2294 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002295 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002296 SkRect bounds;
2297 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002298
2299 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002300 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2301 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2302 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2303 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002304
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002306
bsalomon4b4a7cc2016-07-08 04:42:54 -07002307 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2308 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002309 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002310
Brian Salomon289e3d82016-12-14 15:52:56 -05002311 bool isStrokeOnly =
2312 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002313 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2314
jvanverthc3d0e422016-08-25 08:12:35 -07002315 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002316 if (hasStroke) {
2317 if (SkStrokeRec::kHairline_Style == style) {
2318 scaledStroke.set(1, 1);
2319 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002320 scaledStroke.fX = SkScalarAbs(
2321 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2322 scaledStroke.fY = SkScalarAbs(
2323 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002324 }
2325
jvanverthc3d0e422016-08-25 08:12:35 -07002326 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2327 // for non-circular rrects, if half of strokewidth is greater than radius,
2328 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002329 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2330 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002331 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002332 }
2333 }
2334
2335 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2336 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2337 // patch will have fractional coverage. This only matters when the interior is actually filled.
2338 // We could consider falling back to rect rendering here, since a tiny radius is
2339 // indistinguishable from a square corner.
2340 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002341 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002342 }
2343
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002344 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002345 if (isCircular) {
Brian Salomon649a3412017-03-09 13:50:43 -05002346 return std::unique_ptr<GrMeshDrawOp>(new CircularRRectOp(
Brian Salomonf8334782017-01-03 09:42:58 -05002347 color, needsDistance, viewMatrix, bounds, xRadius, scaledStroke.fX, isStrokeOnly));
Brian Salomon289e3d82016-12-14 15:52:56 -05002348 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002349 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002350 return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
2351 isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002352 }
joshualitt3e708c52015-04-30 13:49:27 -07002353}
2354
Brian Salomon649a3412017-03-09 13:50:43 -05002355std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
2356 bool needsDistance,
2357 const SkMatrix& viewMatrix,
2358 const SkRRect& rrect,
2359 const SkStrokeRec& stroke,
2360 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002361 if (rrect.isOval()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002362 return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002363 }
2364
2365 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002366 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002367 }
2368
Brian Salomon289e3d82016-12-14 15:52:56 -05002369 return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002370}
joshualitt3e708c52015-04-30 13:49:27 -07002371
bsalomon4b4a7cc2016-07-08 04:42:54 -07002372///////////////////////////////////////////////////////////////////////////////
2373
Brian Salomon649a3412017-03-09 13:50:43 -05002374std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
2375 const SkMatrix& viewMatrix,
2376 const SkRect& oval,
2377 const SkStrokeRec& stroke,
2378 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002379 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002380 SkScalar width = oval.width();
2381 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2382 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002383 return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002384 }
2385
Stan Ilieveb868aa2017-02-21 11:06:16 -05002386 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002387 if (viewMatrix.rectStaysRect()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002388 return EllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002389 }
2390
Stan Ilieveb868aa2017-02-21 11:06:16 -05002391 // Otherwise, if we have shader derivative support, render as device-independent
2392 if (shaderCaps->shaderDerivativeSupport()) {
2393 return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
2394 }
2395
bsalomon4b4a7cc2016-07-08 04:42:54 -07002396 return nullptr;
2397}
2398
2399///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002400
Brian Salomon649a3412017-03-09 13:50:43 -05002401std::unique_ptr<GrMeshDrawOp> GrOvalOpFactory::MakeArcOp(GrColor color, const SkMatrix& viewMatrix,
2402 const SkRect& oval, SkScalar startAngle,
2403 SkScalar sweepAngle, bool useCenter,
2404 const GrStyle& style,
2405 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002406 SkASSERT(!oval.isEmpty());
2407 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002408 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002409 if (SkScalarAbs(sweepAngle) >= 360.f) {
2410 return nullptr;
2411 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002412 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2413 return nullptr;
2414 }
2415 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002416 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2417 useCenter};
2418 return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002419}
2420
2421///////////////////////////////////////////////////////////////////////////////
2422
Hal Canary6f6961e2017-01-31 13:50:44 -05002423#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002424
Brian Salomon5ec9def2016-12-20 15:34:05 -05002425DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002426 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002427 SkScalar rotate = random->nextSScalar1() * 360.f;
2428 SkScalar translateX = random->nextSScalar1() * 1000.f;
2429 SkScalar translateY = random->nextSScalar1() * 1000.f;
2430 SkScalar scale = random->nextSScalar1() * 100.f;
2431 SkMatrix viewMatrix;
2432 viewMatrix.setRotate(rotate);
2433 viewMatrix.postTranslate(translateX, translateY);
2434 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002435 GrColor color = GrRandomColor(random);
2436 SkRect circle = GrTest::TestSquare(random);
2437 SkPoint center = {circle.centerX(), circle.centerY()};
2438 SkScalar radius = circle.width() / 2.f;
2439 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002440 CircleOp::ArcParams arcParamsTmp;
2441 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002442 if (random->nextBool()) {
2443 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002444 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2445 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002446 arcParams = &arcParamsTmp;
2447 }
Brian Salomon649a3412017-03-09 13:50:43 -05002448 std::unique_ptr<GrMeshDrawOp> op = CircleOp::Make(color, viewMatrix, center, radius,
2449 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002450 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002451 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002452 }
2453 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002454}
2455
Brian Salomon5ec9def2016-12-20 15:34:05 -05002456DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002457 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2458 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002459 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002460 return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002461}
2462
Brian Salomon5ec9def2016-12-20 15:34:05 -05002463DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002464 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2465 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002466 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002467 return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002468}
2469
Brian Salomon5ec9def2016-12-20 15:34:05 -05002470DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002471 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2472 GrColor color = GrRandomColor(random);
2473 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002474 bool needsDistance = random->nextBool();
Brian Salomon5ec9def2016-12-20 15:34:05 -05002475 return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002476}
2477
2478#endif