blob: 71f8bb089f911678a466cd777e43b639b6b38e58 [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
Brian Salomond3b65972017-03-22 12:05:03 -0400106 ~CircleGeometryProcessor() override {}
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
Brian Salomond3b65972017-03-22 12:05:03 -0400277 ~EllipseGeometryProcessor() override {}
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
Brian Salomond3b65972017-03-22 12:05:03 -0400419 ~DIEllipseGeometryProcessor() override {}
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 Salomond3ccb0a2017-04-03 10:38:00 -0400611class CircleOp final : public GrLegacyMeshDrawOp {
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 Salomond3ccb0a2017-04-03 10:38:00 -0400621 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
622 SkPoint center, SkScalar radius,
623 const GrStyle& style,
624 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700625 SkASSERT(circle_stays_circle(viewMatrix));
626 const SkStrokeRec& stroke = style.strokeRec();
627 if (style.hasPathEffect()) {
628 return nullptr;
629 }
630 SkStrokeRec::Style recStyle = stroke.getStyle();
631 if (arcParams) {
632 // Arc support depends on the style.
633 switch (recStyle) {
634 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500635 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700636 return nullptr;
637 case SkStrokeRec::kFill_Style:
638 // This supports all fills.
639 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500640 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700641 case SkStrokeRec::kHairline_Style:
642 // Strokes that don't use the center point are supported with butt cap.
643 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
644 return nullptr;
645 }
646 break;
647 }
648 }
649
bsalomon4b4a7cc2016-07-08 04:42:54 -0700650 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700651 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700652 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800653
Brian Salomon289e3d82016-12-14 15:52:56 -0500654 bool isStrokeOnly =
655 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700656 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700657
jvanverth6ca48822016-10-07 06:57:32 -0700658 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700659 SkScalar outerRadius = radius;
660 SkScalar halfWidth = 0;
661 if (hasStroke) {
662 if (SkScalarNearlyZero(strokeWidth)) {
663 halfWidth = SK_ScalarHalf;
664 } else {
665 halfWidth = SkScalarHalf(strokeWidth);
666 }
667
668 outerRadius += halfWidth;
669 if (isStrokeOnly) {
670 innerRadius = radius - halfWidth;
671 }
672 }
673
674 // The radii are outset for two reasons. First, it allows the shader to simply perform
675 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
676 // Second, the outer radius is used to compute the verts of the bounding box that is
677 // rendered and the outset ensures the box will cover all partially covered by the circle.
678 outerRadius += SK_ScalarHalf;
679 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700680 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomonf8334782017-01-03 09:42:58 -0500681 std::unique_ptr<CircleOp> op(new CircleOp());
Brian Salomon289e3d82016-12-14 15:52:56 -0500682 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700683
bsalomon4f3a0ca2016-08-22 13:14:26 -0700684 // This makes every point fully inside the intersection plane.
685 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
686 // This makes every point fully outside the union plane.
687 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
688 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
689 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700690 if (arcParams) {
691 // The shader operates in a space where the circle is translated to be centered at the
692 // origin. Here we compute points on the unit circle at the starting and ending angles.
693 SkPoint startPoint, stopPoint;
694 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
695 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
696 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
697 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
698 // radial lines. However, in both cases we have to be careful about the half-circle.
699 // case. In that case the two radial lines are equal and so that edge gets clipped
700 // twice. Since the shared edge goes through the center we fall back on the useCenter
701 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500702 bool useCenter =
703 (arcParams->fUseCenter || isStrokeOnly) &&
704 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700705 if (useCenter) {
706 SkVector norm0 = {startPoint.fY, -startPoint.fX};
707 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
708 if (arcParams->fSweepAngleRadians > 0) {
709 norm0.negate();
710 } else {
711 norm1.negate();
712 }
Brian Salomon289e3d82016-12-14 15:52:56 -0500713 op->fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700714 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500715 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700716 color,
717 innerRadius,
718 outerRadius,
719 {norm0.fX, norm0.fY, 0.5f},
720 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
721 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700722 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500723 stroked});
724 op->fClipPlaneIsect = false;
725 op->fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500727 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700728 color,
729 innerRadius,
730 outerRadius,
731 {norm0.fX, norm0.fY, 0.5f},
732 {norm1.fX, norm1.fY, 0.5f},
733 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700734 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500735 stroked});
736 op->fClipPlaneIsect = true;
737 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700738 }
739 } else {
740 // We clip to a secant of the original circle.
741 startPoint.scale(radius);
742 stopPoint.scale(radius);
743 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
744 norm.normalize();
745 if (arcParams->fSweepAngleRadians > 0) {
746 norm.negate();
747 }
748 SkScalar d = -norm.dot(startPoint) + 0.5f;
749
Brian Salomon289e3d82016-12-14 15:52:56 -0500750 op->fGeoData.emplace_back(
751 Geometry{color,
752 innerRadius,
753 outerRadius,
754 {norm.fX, norm.fY, d},
755 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
756 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
757 devBounds,
758 stroked});
759 op->fClipPlane = true;
760 op->fClipPlaneIsect = false;
761 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700762 }
763 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500764 op->fGeoData.emplace_back(
765 Geometry{color,
766 innerRadius,
767 outerRadius,
768 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
769 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
770 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
771 devBounds,
772 stroked});
773 op->fClipPlane = false;
774 op->fClipPlaneIsect = false;
775 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700776 }
bsalomon88cf17d2016-07-08 06:40:56 -0700777 // Use the original radius and stroke radius for the bounds so that it does not include the
778 // AA bloat.
779 radius += halfWidth;
Brian Salomon289e3d82016-12-14 15:52:56 -0500780 op->setBounds(
781 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
782 HasAABloat::kYes, IsZeroArea::kNo);
783 op->fVertCount = circle_type_to_vert_count(stroked);
784 op->fIndexCount = circle_type_to_index_count(stroked);
785 op->fAllFill = !stroked;
786 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -0800787 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700788
Brian Salomon289e3d82016-12-14 15:52:56 -0500789 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800790
robertphillipse004bfc2015-11-16 09:06:59 -0800791 SkString dumpInfo() const override {
792 SkString string;
793 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500794 string.appendf(
795 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
796 "InnerRad: %.2f, OuterRad: %.2f\n",
797 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
798 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
799 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800800 }
Brian Salomon7c3e7182016-12-01 09:35:30 -0500801 string.append(DumpPipelineInfo(*this->pipeline()));
robertphillipse004bfc2015-11-16 09:06:59 -0800802 string.append(INHERITED::dumpInfo());
803 return string;
804 }
805
bsalomone46f9fe2015-08-18 06:05:14 -0700806private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500807 CircleOp() : INHERITED(ClassID()) {}
Brian Salomon92aee3d2016-12-21 09:20:25 -0500808
Brian Salomona811b122017-03-30 08:21:32 -0400809 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
810 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -0400811 color->setToConstant(fGeoData[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -0400812 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -0500813 }
814
Brian Salomone7d30482017-03-29 12:09:15 -0400815 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -0500816 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
817 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800818 fViewMatrixIfUsingLocalCoords.reset();
819 }
joshualitt76e7fb62015-02-11 08:52:27 -0800820 }
821
joshualitt144c3c82015-11-30 12:30:13 -0800822 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800823 SkMatrix localMatrix;
824 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800825 return;
826 }
827
828 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500829 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
830 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700831
832 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500833 SkPoint fPos;
834 GrColor fColor;
835 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700836 SkScalar fOuterRadius;
837 SkScalar fInnerRadius;
838 // These planes may or may not be present in the vertex buffer.
839 SkScalar fHalfPlanes[3][3];
840 };
joshualitt76e7fb62015-02-11 08:52:27 -0800841
joshualitt76e7fb62015-02-11 08:52:27 -0800842 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800843 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500844 SkASSERT(vertexStride ==
845 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
846 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
847 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700848
849 const GrBuffer* vertexBuffer;
850 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500851 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
852 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700853 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700854 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800855 return;
856 }
857
jvanverth6ca48822016-10-07 06:57:32 -0700858 const GrBuffer* indexBuffer = nullptr;
859 int firstIndex = 0;
860 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
861 if (!indices) {
862 SkDebugf("Could not allocate indices\n");
863 return;
864 }
865
866 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800867 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800868 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800869
brianosmanbb2ff942016-02-11 14:15:18 -0800870 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700871 SkScalar innerRadius = geom.fInnerRadius;
872 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800873
bsalomonb5238a72015-05-05 07:49:49 -0700874 const SkRect& bounds = geom.fDevBounds;
Brian Salomon289e3d82016-12-14 15:52:56 -0500875 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
876 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
877 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
878 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
879 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
880 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
881 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
882 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800883
884 // The inner radius in the vertex data must be specified in normalized space.
885 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700886
887 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500888 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700889 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500890
Brian Salomon289e3d82016-12-14 15:52:56 -0500891 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700892 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700893 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700894 v0->fOuterRadius = outerRadius;
895 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800896
Brian Salomon289e3d82016-12-14 15:52:56 -0500897 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700899 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700900 v1->fOuterRadius = outerRadius;
901 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800902
Brian Salomon289e3d82016-12-14 15:52:56 -0500903 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700904 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700905 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700906 v2->fOuterRadius = outerRadius;
907 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800908
Brian Salomon289e3d82016-12-14 15:52:56 -0500909 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700910 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700911 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700912 v3->fOuterRadius = outerRadius;
913 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800914
Brian Salomon289e3d82016-12-14 15:52:56 -0500915 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700916 v4->fColor = color;
917 v4->fOffset = SkPoint::Make(octOffset, 1);
918 v4->fOuterRadius = outerRadius;
919 v4->fInnerRadius = innerRadius;
920
Brian Salomon289e3d82016-12-14 15:52:56 -0500921 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700922 v5->fColor = color;
923 v5->fOffset = SkPoint::Make(-octOffset, 1);
924 v5->fOuterRadius = outerRadius;
925 v5->fInnerRadius = innerRadius;
926
Brian Salomon289e3d82016-12-14 15:52:56 -0500927 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700928 v6->fColor = color;
929 v6->fOffset = SkPoint::Make(-1, octOffset);
930 v6->fOuterRadius = outerRadius;
931 v6->fInnerRadius = innerRadius;
932
Brian Salomon289e3d82016-12-14 15:52:56 -0500933 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700934 v7->fColor = color;
935 v7->fOffset = SkPoint::Make(-1, -octOffset);
936 v7->fOuterRadius = outerRadius;
937 v7->fInnerRadius = innerRadius;
938
bsalomon4f3a0ca2016-08-22 13:14:26 -0700939 if (fClipPlane) {
940 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
942 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
943 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700944 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
945 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
946 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
947 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700948 }
949 int unionIdx = 1;
950 if (fClipPlaneIsect) {
951 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
953 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
954 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700955 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
956 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
957 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
958 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700959 unionIdx = 2;
960 }
961 if (fClipPlaneUnion) {
962 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
964 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
965 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700966 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
967 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
968 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
969 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700970 }
jvanverth6ca48822016-10-07 06:57:32 -0700971
972 if (geom.fStroked) {
973 // compute the inner ring
974 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
975 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
976 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
977 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
978 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
979 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
980 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
981 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
982
983 // cosine and sine of pi/8
984 SkScalar c = 0.923579533f;
985 SkScalar s = 0.382683432f;
986 SkScalar r = geom.fInnerRadius;
987
Brian Salomon289e3d82016-12-14 15:52:56 -0500988 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700989 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500990 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700991 v0->fOuterRadius = outerRadius;
992 v0->fInnerRadius = innerRadius;
993
Brian Salomon289e3d82016-12-14 15:52:56 -0500994 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700995 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500996 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700997 v1->fOuterRadius = outerRadius;
998 v1->fInnerRadius = innerRadius;
999
Brian Salomon289e3d82016-12-14 15:52:56 -05001000 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001001 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001002 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001003 v2->fOuterRadius = outerRadius;
1004 v2->fInnerRadius = innerRadius;
1005
Brian Salomon289e3d82016-12-14 15:52:56 -05001006 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001007 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001008 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001009 v3->fOuterRadius = outerRadius;
1010 v3->fInnerRadius = innerRadius;
1011
Brian Salomon289e3d82016-12-14 15:52:56 -05001012 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001013 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001014 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001015 v4->fOuterRadius = outerRadius;
1016 v4->fInnerRadius = innerRadius;
1017
Brian Salomon289e3d82016-12-14 15:52:56 -05001018 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001019 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001020 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001021 v5->fOuterRadius = outerRadius;
1022 v5->fInnerRadius = innerRadius;
1023
Brian Salomon289e3d82016-12-14 15:52:56 -05001024 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001025 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001026 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001027 v6->fOuterRadius = outerRadius;
1028 v6->fInnerRadius = innerRadius;
1029
Brian Salomon289e3d82016-12-14 15:52:56 -05001030 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001031 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001032 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001033 v7->fOuterRadius = outerRadius;
1034 v7->fInnerRadius = innerRadius;
1035
1036 if (fClipPlane) {
1037 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1040 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1041 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1042 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1043 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1044 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1045 }
1046 int unionIdx = 1;
1047 if (fClipPlaneIsect) {
1048 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1051 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1052 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1053 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1054 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1055 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1056 unionIdx = 2;
1057 }
1058 if (fClipPlaneUnion) {
1059 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1062 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1063 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1064 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1065 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1066 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1067 }
1068 } else {
1069 // filled
1070 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1071 v8->fPos = center;
1072 v8->fColor = color;
1073 v8->fOffset = SkPoint::Make(0, 0);
1074 v8->fOuterRadius = outerRadius;
1075 v8->fInnerRadius = innerRadius;
1076 if (fClipPlane) {
1077 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1078 }
1079 int unionIdx = 1;
1080 if (fClipPlaneIsect) {
1081 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1082 unionIdx = 2;
1083 }
1084 if (fClipPlaneUnion) {
1085 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1086 }
1087 }
1088
1089 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1090 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1091 for (int i = 0; i < primIndexCount; ++i) {
1092 *indices++ = primIndices[i] + currStartVertex;
1093 }
1094
1095 currStartVertex += circle_type_to_vert_count(geom.fStroked);
Brian Salomon289e3d82016-12-14 15:52:56 -05001096 vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001097 }
jvanverth6ca48822016-10-07 06:57:32 -07001098
1099 GrMesh mesh;
1100 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1101 firstIndex, fVertCount, fIndexCount);
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001102 target->draw(gp.get(), this->pipeline(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001103 }
1104
Brian Salomon25a88092016-12-01 09:36:50 -05001105 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001106 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001107
1108 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001109 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001110 return false;
1111 }
1112
bsalomonabd30f52015-08-13 13:34:48 -07001113 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1114 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001115 return false;
1116 }
1117
jvanverth6ca48822016-10-07 06:57:32 -07001118 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001119 return false;
1120 }
1121
Brian Salomon289e3d82016-12-14 15:52:56 -05001122 // Because we've set up the ops that don't use the planes with noop values
1123 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001124 fClipPlane |= that->fClipPlane;
1125 fClipPlaneIsect |= that->fClipPlaneIsect;
1126 fClipPlaneUnion |= that->fClipPlaneUnion;
1127
bsalomoncdaa97b2016-03-08 08:30:14 -08001128 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001129 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001130 fVertCount += that->fVertCount;
1131 fIndexCount += that->fIndexCount;
1132 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001133 return true;
1134 }
1135
bsalomon4b4a7cc2016-07-08 04:42:54 -07001136 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05001137 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001138 SkScalar fInnerRadius;
1139 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001140 SkScalar fClipPlane[3];
1141 SkScalar fIsectPlane[3];
1142 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001143 SkRect fDevBounds;
1144 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001145 };
1146
jvanverth6ca48822016-10-07 06:57:32 -07001147 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05001148 SkMatrix fViewMatrixIfUsingLocalCoords;
1149 int fVertCount;
1150 int fIndexCount;
1151 bool fAllFill;
1152 bool fClipPlane;
1153 bool fClipPlaneIsect;
1154 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001155
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001156 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001157};
1158
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001159///////////////////////////////////////////////////////////////////////////////
1160
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001161class EllipseOp : public GrLegacyMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001162public:
Brian Salomon25a88092016-12-01 09:36:50 -05001163 DEFINE_OP_CLASS_ID
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001164 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
1165 const SkRect& ellipse,
1166 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001167 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001168
bsalomon4b4a7cc2016-07-08 04:42:54 -07001169 // do any matrix crunching before we reset the draw state for device coords
1170 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1171 viewMatrix.mapPoints(&center, 1);
1172 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1173 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon289e3d82016-12-14 15:52:56 -05001174 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
Jim Van Verthd952a992017-04-20 17:25:26 -04001175 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1176 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
Brian Salomon289e3d82016-12-14 15:52:56 -05001177 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001178
bsalomon4b4a7cc2016-07-08 04:42:54 -07001179 // do (potentially) anisotropic mapping of stroke
1180 SkVector scaledStroke;
1181 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001182 scaledStroke.fX = SkScalarAbs(
1183 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1184 scaledStroke.fY = SkScalarAbs(
1185 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001186
1187 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001188 bool isStrokeOnly =
1189 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001190 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1191
1192 SkScalar innerXRadius = 0;
1193 SkScalar innerYRadius = 0;
1194 if (hasStroke) {
1195 if (SkScalarNearlyZero(scaledStroke.length())) {
1196 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1197 } else {
1198 scaledStroke.scale(SK_ScalarHalf);
1199 }
1200
1201 // we only handle thick strokes for near-circular ellipses
1202 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001203 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001204 return nullptr;
1205 }
1206
1207 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001208 if (scaledStroke.fX * (yRadius * yRadius) <
1209 (scaledStroke.fY * scaledStroke.fY) * xRadius ||
1210 scaledStroke.fY * (xRadius * xRadius) <
1211 (scaledStroke.fX * scaledStroke.fX) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001212 return nullptr;
1213 }
1214
1215 // this is legit only if scale & translation (which should be the case at the moment)
1216 if (isStrokeOnly) {
1217 innerXRadius = xRadius - scaledStroke.fX;
1218 innerYRadius = yRadius - scaledStroke.fY;
1219 }
1220
1221 xRadius += scaledStroke.fX;
1222 yRadius += scaledStroke.fY;
1223 }
1224
Brian Salomonf8334782017-01-03 09:42:58 -05001225 std::unique_ptr<EllipseOp> op(new EllipseOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05001226 op->fGeoData.emplace_back(
1227 Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
1228 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1229 center.fX + xRadius, center.fY + yRadius)});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001230
Brian Salomon289e3d82016-12-14 15:52:56 -05001231 op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001232
bsalomon4b4a7cc2016-07-08 04:42:54 -07001233 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon289e3d82016-12-14 15:52:56 -05001234 op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001235
Brian Salomon289e3d82016-12-14 15:52:56 -05001236 op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1237 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
1238 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -08001239 }
joshualitt76e7fb62015-02-11 08:52:27 -08001240
Brian Salomon289e3d82016-12-14 15:52:56 -05001241 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001242
Brian Salomon7c3e7182016-12-01 09:35:30 -05001243 SkString dumpInfo() const override {
1244 SkString string;
1245 string.appendf("Stroked: %d\n", fStroked);
1246 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001247 string.appendf(
1248 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1249 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1250 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1251 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1252 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001253 }
1254 string.append(DumpPipelineInfo(*this->pipeline()));
1255 string.append(INHERITED::dumpInfo());
1256 return string;
1257 }
1258
bsalomone46f9fe2015-08-18 06:05:14 -07001259private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001260 EllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001261
Brian Salomona811b122017-03-30 08:21:32 -04001262 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
1263 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -04001264 color->setToConstant(fGeoData[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -04001265 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -05001266 }
1267
Brian Salomone7d30482017-03-29 12:09:15 -04001268 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -05001269 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001270 fViewMatrixIfUsingLocalCoords.reset();
1271 }
joshualitt76e7fb62015-02-11 08:52:27 -08001272 }
1273
joshualitt144c3c82015-11-30 12:30:13 -08001274 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001275 SkMatrix localMatrix;
1276 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001277 return;
1278 }
1279
1280 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001281 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001282
joshualitt76e7fb62015-02-11 08:52:27 -08001283 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001284 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001285 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001286 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon289e3d82016-12-14 15:52:56 -05001287 EllipseVertex* verts =
1288 reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001289 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001290 return;
1291 }
1292
bsalomon8415abe2015-05-04 11:41:41 -07001293 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001294 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001295
brianosmanbb2ff942016-02-11 14:15:18 -08001296 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001297 SkScalar xRadius = geom.fXRadius;
1298 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001299
1300 // Compute the reciprocals of the radii here to save time in the shader
1301 SkScalar xRadRecip = SkScalarInvert(xRadius);
1302 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001303 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1304 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001305
bsalomonb5238a72015-05-05 07:49:49 -07001306 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001307
vjiaoblack977996d2016-06-30 12:20:54 -07001308 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1309 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1310 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1311
joshualitt76e7fb62015-02-11 08:52:27 -08001312 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon289e3d82016-12-14 15:52:56 -05001313 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001314 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001315 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001316 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1317 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1318
Brian Salomon289e3d82016-12-14 15:52:56 -05001319 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001320 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001321 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001322 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1323 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1324
1325 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001326 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001327 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001328 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1329 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1330
1331 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001332 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001333 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001334 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1335 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1336
bsalomonb5238a72015-05-05 07:49:49 -07001337 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001338 }
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001339 helper.recordDraw(target, gp.get(), this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001340 }
1341
Brian Salomon25a88092016-12-01 09:36:50 -05001342 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001343 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001344
1345 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1346 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001347 return false;
1348 }
1349
bsalomoncdaa97b2016-03-08 08:30:14 -08001350 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001351 return false;
1352 }
1353
bsalomoncdaa97b2016-03-08 08:30:14 -08001354 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001355 return false;
1356 }
1357
bsalomoncdaa97b2016-03-08 08:30:14 -08001358 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001359 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001360 return true;
1361 }
1362
bsalomon4b4a7cc2016-07-08 04:42:54 -07001363 struct Geometry {
1364 GrColor fColor;
1365 SkScalar fXRadius;
1366 SkScalar fYRadius;
1367 SkScalar fInnerXRadius;
1368 SkScalar fInnerYRadius;
1369 SkRect fDevBounds;
1370 };
joshualitt76e7fb62015-02-11 08:52:27 -08001371
Brian Salomon289e3d82016-12-14 15:52:56 -05001372 bool fStroked;
1373 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001374 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001375
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001376 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001377};
1378
joshualitt76e7fb62015-02-11 08:52:27 -08001379/////////////////////////////////////////////////////////////////////////////////////////////////
1380
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001381class DIEllipseOp : public GrLegacyMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001382public:
Brian Salomon25a88092016-12-01 09:36:50 -05001383 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001384
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001385 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
1386 const SkMatrix& viewMatrix,
1387 const SkRect& ellipse,
1388 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001389 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1390 SkScalar xRadius = SkScalarHalf(ellipse.width());
1391 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001392
bsalomon4b4a7cc2016-07-08 04:42:54 -07001393 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001394 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
1395 ? DIEllipseStyle::kStroke
1396 : (SkStrokeRec::kHairline_Style == style)
1397 ? DIEllipseStyle::kHairline
1398 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001399
1400 SkScalar innerXRadius = 0;
1401 SkScalar innerYRadius = 0;
1402 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1403 SkScalar strokeWidth = stroke.getWidth();
1404
1405 if (SkScalarNearlyZero(strokeWidth)) {
1406 strokeWidth = SK_ScalarHalf;
1407 } else {
1408 strokeWidth *= SK_ScalarHalf;
1409 }
1410
1411 // we only handle thick strokes for near-circular ellipses
1412 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001413 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001414 return nullptr;
1415 }
1416
1417 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001418 if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
1419 strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001420 return nullptr;
1421 }
1422
1423 // set inner radius (if needed)
1424 if (SkStrokeRec::kStroke_Style == style) {
1425 innerXRadius = xRadius - strokeWidth;
1426 innerYRadius = yRadius - strokeWidth;
1427 }
1428
1429 xRadius += strokeWidth;
1430 yRadius += strokeWidth;
1431 }
1432 if (DIEllipseStyle::kStroke == dieStyle) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001433 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
1434 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001435 }
1436
1437 // This expands the outer rect so that after CTM we end up with a half-pixel border
1438 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1439 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1440 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1441 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001442 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1443 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001444
Brian Salomonf8334782017-01-03 09:42:58 -05001445 std::unique_ptr<DIEllipseOp> op(new DIEllipseOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05001446 op->fGeoData.emplace_back(Geometry{
1447 viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
1448 dieStyle,
1449 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1450 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
1451 op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1452 IsZeroArea::kNo);
1453 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08001454 }
1455
Brian Salomon289e3d82016-12-14 15:52:56 -05001456 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001457
Brian Salomon7c3e7182016-12-01 09:35:30 -05001458 SkString dumpInfo() const override {
1459 SkString string;
1460 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001461 string.appendf(
1462 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1463 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1464 "GeoDY: %.2f\n",
1465 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1466 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1467 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001468 }
1469 string.append(DumpPipelineInfo(*this->pipeline()));
1470 string.append(INHERITED::dumpInfo());
1471 return string;
1472 }
1473
bsalomone46f9fe2015-08-18 06:05:14 -07001474private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001475 DIEllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001476
Brian Salomona811b122017-03-30 08:21:32 -04001477 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
1478 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -04001479 color->setToConstant(fGeoData[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -04001480 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -05001481 }
1482
Brian Salomone7d30482017-03-29 12:09:15 -04001483 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -05001484 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1485 fUsesLocalCoords = optimizations.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001486 }
1487
joshualitt144c3c82015-11-30 12:30:13 -08001488 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001489 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001490 sk_sp<GrGeometryProcessor> gp(
1491 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001492
joshualitt76e7fb62015-02-11 08:52:27 -08001493 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001494 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001495 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001496 QuadHelper helper;
1497 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05001498 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001499 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001500 return;
1501 }
1502
joshualitt76e7fb62015-02-11 08:52:27 -08001503 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001504 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001505
brianosmanbb2ff942016-02-11 14:15:18 -08001506 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001507 SkScalar xRadius = geom.fXRadius;
1508 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001509
bsalomonb5238a72015-05-05 07:49:49 -07001510 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001511
1512 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001513 SkScalar offsetDx = geom.fGeoDx / xRadius;
1514 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001515
reed80ea19c2015-05-12 10:37:34 -07001516 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1517 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001518
1519 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001520 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001521 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1522 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1523
Brian Salomon289e3d82016-12-14 15:52:56 -05001524 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001525 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001526 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1527 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1528
1529 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001530 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001531 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1532 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1533
1534 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001535 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001536 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1537 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1538
bsalomonb5238a72015-05-05 07:49:49 -07001539 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001540 }
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001541 helper.recordDraw(target, gp.get(), this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001542 }
halcanary9d524f22016-03-29 09:03:52 -07001543
Brian Salomon25a88092016-12-01 09:36:50 -05001544 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001545 DIEllipseOp* that = t->cast<DIEllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001546 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1547 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001548 return false;
1549 }
1550
bsalomoncdaa97b2016-03-08 08:30:14 -08001551 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001552 return false;
1553 }
1554
joshualittd96a67b2015-05-05 14:09:05 -07001555 // TODO rewrite to allow positioning on CPU
1556 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001557 return false;
1558 }
1559
bsalomoncdaa97b2016-03-08 08:30:14 -08001560 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001561 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001562 return true;
1563 }
1564
joshualitt76e7fb62015-02-11 08:52:27 -08001565 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001566 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001567
bsalomon4b4a7cc2016-07-08 04:42:54 -07001568 struct Geometry {
1569 SkMatrix fViewMatrix;
1570 GrColor fColor;
1571 SkScalar fXRadius;
1572 SkScalar fYRadius;
1573 SkScalar fInnerXRadius;
1574 SkScalar fInnerYRadius;
1575 SkScalar fGeoDx;
1576 SkScalar fGeoDy;
1577 DIEllipseStyle fStyle;
1578 SkRect fBounds;
1579 };
1580
Brian Salomon289e3d82016-12-14 15:52:56 -05001581 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001582 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001583
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001584 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001585};
1586
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001587///////////////////////////////////////////////////////////////////////////////
1588
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001589// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001590//
1591// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1592// ____________
1593// |_|________|_|
1594// | | | |
1595// | | | |
1596// | | | |
1597// |_|________|_|
1598// |_|________|_|
1599//
1600// For strokes, we don't draw the center quad.
1601//
1602// For circular roundrects, in the case where the stroke width is greater than twice
1603// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001604// in the center. The shared vertices are duplicated so we can set a different outer radius
1605// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001606// ____________
1607// |_|________|_|
1608// | |\ ____ /| |
1609// | | | | | |
1610// | | |____| | |
1611// |_|/______\|_|
1612// |_|________|_|
1613//
1614// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001615//
1616// For filled rrects that need to provide a distance vector we resuse the overstroke
1617// geometry but make the inner rect degenerate (either a point or a horizontal or
1618// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001619
jvanverth84839f62016-08-29 10:16:40 -07001620static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001621 // clang-format off
1622 // overstroke quads
1623 // we place this at the beginning so that we can skip these indices when rendering normally
1624 16, 17, 19, 16, 19, 18,
1625 19, 17, 23, 19, 23, 21,
1626 21, 23, 22, 21, 22, 20,
1627 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001628
Brian Salomon289e3d82016-12-14 15:52:56 -05001629 // corners
1630 0, 1, 5, 0, 5, 4,
1631 2, 3, 7, 2, 7, 6,
1632 8, 9, 13, 8, 13, 12,
1633 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001634
Brian Salomon289e3d82016-12-14 15:52:56 -05001635 // edges
1636 1, 2, 6, 1, 6, 5,
1637 4, 5, 9, 4, 9, 8,
1638 6, 7, 11, 6, 11, 10,
1639 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001640
Brian Salomon289e3d82016-12-14 15:52:56 -05001641 // center
1642 // we place this at the end so that we can ignore these indices when not rendering as filled
1643 5, 6, 10, 5, 10, 9,
1644 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001645};
Brian Salomon289e3d82016-12-14 15:52:56 -05001646
jvanverth84839f62016-08-29 10:16:40 -07001647// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001648static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001649
jvanverth84839f62016-08-29 10:16:40 -07001650// overstroke count is arraysize minus the center indices
1651static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1652// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001653static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001654// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001655static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1656static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001657static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001658
jvanverthc3d0e422016-08-25 08:12:35 -07001659enum RRectType {
1660 kFill_RRectType,
1661 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001662 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001663 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001664};
1665
jvanverth84839f62016-08-29 10:16:40 -07001666static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001667 switch (type) {
1668 case kFill_RRectType:
1669 case kStroke_RRectType:
1670 return kVertsPerStandardRRect;
1671 case kOverstroke_RRectType:
1672 case kFillWithDist_RRectType:
1673 return kVertsPerOverstrokeRRect;
1674 }
1675 SkFAIL("Invalid type");
1676 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001677}
1678
1679static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001680 switch (type) {
1681 case kFill_RRectType:
1682 return kIndicesPerFillRRect;
1683 case kStroke_RRectType:
1684 return kIndicesPerStrokeRRect;
1685 case kOverstroke_RRectType:
1686 case kFillWithDist_RRectType:
1687 return kIndicesPerOverstrokeRRect;
1688 }
1689 SkFAIL("Invalid type");
1690 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001691}
1692
1693static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001694 switch (type) {
1695 case kFill_RRectType:
1696 case kStroke_RRectType:
1697 return gStandardRRectIndices;
1698 case kOverstroke_RRectType:
1699 case kFillWithDist_RRectType:
1700 return gOverstrokeRRectIndices;
1701 }
1702 SkFAIL("Invalid type");
1703 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001704}
1705
joshualitt76e7fb62015-02-11 08:52:27 -08001706///////////////////////////////////////////////////////////////////////////////////////////////////
1707
Robert Phillips79839d42016-10-06 15:03:34 -04001708// For distance computations in the interior of filled rrects we:
1709//
1710// add a interior degenerate (point or line) rect
1711// each vertex of that rect gets -outerRad as its radius
1712// this makes the computation of the distance to the outer edge be negative
1713// negative values are caught and then handled differently in the GP's onEmitCode
1714// each vertex is also given the normalized x & y distance from the interior rect's edge
1715// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1716
Brian Salomond3ccb0a2017-04-03 10:38:00 -04001717class CircularRRectOp : public GrLegacyMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001718public:
Brian Salomon25a88092016-12-01 09:36:50 -05001719 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001720
bsalomon4b4a7cc2016-07-08 04:42:54 -07001721 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1722 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05001723 CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1724 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1725 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001726 SkRect bounds = devRect;
1727 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1728 SkScalar innerRadius = 0.0f;
1729 SkScalar outerRadius = devRadius;
1730 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001731 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001732 if (devStrokeWidth > 0) {
1733 if (SkScalarNearlyZero(devStrokeWidth)) {
1734 halfWidth = SK_ScalarHalf;
1735 } else {
1736 halfWidth = SkScalarHalf(devStrokeWidth);
1737 }
joshualitt76e7fb62015-02-11 08:52:27 -08001738
bsalomon4b4a7cc2016-07-08 04:42:54 -07001739 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001740 // Outset stroke by 1/4 pixel
1741 devStrokeWidth += 0.25f;
1742 // If stroke is greater than width or height, this is still a fill
1743 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001744 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001745 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001746 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001747 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001748 }
1749 outerRadius += halfWidth;
1750 bounds.outset(halfWidth, halfWidth);
1751 }
Robert Phillips79839d42016-10-06 15:03:34 -04001752 if (kFill_RRectType == type && needsDistance) {
1753 type = kFillWithDist_RRectType;
1754 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001755
bsalomon4b4a7cc2016-07-08 04:42:54 -07001756 // The radii are outset for two reasons. First, it allows the shader to simply perform
1757 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1758 // Second, the outer radius is used to compute the verts of the bounding box that is
1759 // rendered and the outset ensures the box will cover all partially covered by the rrect
1760 // corners.
1761 outerRadius += SK_ScalarHalf;
1762 innerRadius -= SK_ScalarHalf;
1763
bsalomon88cf17d2016-07-08 06:40:56 -07001764 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1765
1766 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001767 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1768
Brian Salomon289e3d82016-12-14 15:52:56 -05001769 fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001770 fVertCount = rrect_type_to_vert_count(type);
1771 fIndexCount = rrect_type_to_index_count(type);
1772 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001773 }
1774
Brian Salomon289e3d82016-12-14 15:52:56 -05001775 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001776
jvanverthc3d0e422016-08-25 08:12:35 -07001777 SkString dumpInfo() const override {
1778 SkString string;
1779 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001780 string.appendf(
1781 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1782 "InnerRad: %.2f, OuterRad: %.2f\n",
1783 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1784 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1785 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001786 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001787 string.append(DumpPipelineInfo(*this->pipeline()));
jvanverthc3d0e422016-08-25 08:12:35 -07001788 string.append(INHERITED::dumpInfo());
1789 return string;
1790 }
1791
Brian Salomon92aee3d2016-12-21 09:20:25 -05001792private:
Brian Salomona811b122017-03-30 08:21:32 -04001793 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
1794 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -04001795 color->setToConstant(fGeoData[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -04001796 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
joshualitt76e7fb62015-02-11 08:52:27 -08001797 }
1798
Brian Salomone7d30482017-03-29 12:09:15 -04001799 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -05001800 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1801 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08001802 fViewMatrixIfUsingLocalCoords.reset();
1803 }
joshualitt76e7fb62015-02-11 08:52:27 -08001804 }
1805
Robert Phillips79839d42016-10-06 15:03:34 -04001806 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001807 SkPoint fPos;
1808 GrColor fColor;
1809 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001810 SkScalar fOuterRadius;
1811 SkScalar fInnerRadius;
1812 // No half plane, we don't use it here.
1813 };
1814
Brian Salomon289e3d82016-12-14 15:52:56 -05001815 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1816 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1817 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001818 SkASSERT(smInset < bigInset);
1819
1820 // TL
1821 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1822 (*verts)->fColor = color;
1823 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1824 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001825 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001826 (*verts)++;
1827
1828 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001829 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001830 (*verts)->fColor = color;
1831 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1832 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001833 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001834 (*verts)++;
1835
Brian Salomon289e3d82016-12-14 15:52:56 -05001836 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001837 (*verts)->fColor = color;
1838 (*verts)->fOffset = SkPoint::Make(0, 0);
1839 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001840 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001841 (*verts)++;
1842
Brian Salomon289e3d82016-12-14 15:52:56 -05001843 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001844 (*verts)->fColor = color;
1845 (*verts)->fOffset = SkPoint::Make(0, 0);
1846 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001847 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001848 (*verts)++;
1849
1850 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1851 (*verts)->fColor = color;
1852 (*verts)->fOffset = SkPoint::Make(0, 0);
1853 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001854 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001855 (*verts)++;
1856
1857 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1858 (*verts)->fColor = color;
1859 (*verts)->fOffset = SkPoint::Make(0, 0);
1860 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001861 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001862 (*verts)++;
1863
1864 // BL
1865 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1866 (*verts)->fColor = color;
1867 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1868 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001869 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001870 (*verts)++;
1871
1872 // BR
1873 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1874 (*verts)->fColor = color;
1875 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1876 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001877 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001878 (*verts)++;
1879 }
1880
joshualitt144c3c82015-11-30 12:30:13 -08001881 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001882 // Invert the view matrix as a local matrix (if any other processors require coords).
1883 SkMatrix localMatrix;
1884 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001885 return;
1886 }
1887
1888 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001889 sk_sp<GrGeometryProcessor> gp(
1890 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001891
joshualitt76e7fb62015-02-11 08:52:27 -08001892 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001893 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001894 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001895
jvanverth84839f62016-08-29 10:16:40 -07001896 const GrBuffer* vertexBuffer;
1897 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001898
Brian Salomon289e3d82016-12-14 15:52:56 -05001899 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1900 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001901 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001902 SkDebugf("Could not allocate vertices\n");
1903 return;
1904 }
1905
jvanverth84839f62016-08-29 10:16:40 -07001906 const GrBuffer* indexBuffer = nullptr;
1907 int firstIndex = 0;
1908 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1909 if (!indices) {
1910 SkDebugf("Could not allocate indices\n");
1911 return;
1912 }
1913
1914 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001915 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001916 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001917
brianosmanbb2ff942016-02-11 14:15:18 -08001918 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001919 SkScalar outerRadius = args.fOuterRadius;
1920
egdanielbc227142015-04-21 06:28:08 -07001921 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001922
Brian Salomon289e3d82016-12-14 15:52:56 -05001923 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1924 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001925
Brian Salomon289e3d82016-12-14 15:52:56 -05001926 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001927 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001928 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomon289e3d82016-12-14 15:52:56 -05001929 SkScalar innerRadius =
1930 args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
1931 ? args.fInnerRadius / args.fOuterRadius
1932 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001933 for (int i = 0; i < 4; ++i) {
1934 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001935 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001936 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1937 verts->fOuterRadius = outerRadius;
1938 verts->fInnerRadius = innerRadius;
1939 verts++;
1940
1941 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001942 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001943 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1944 verts->fOuterRadius = outerRadius;
1945 verts->fInnerRadius = innerRadius;
1946 verts++;
1947
1948 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001949 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001950 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1951 verts->fOuterRadius = outerRadius;
1952 verts->fInnerRadius = innerRadius;
1953 verts++;
1954
1955 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001956 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001957 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1958 verts->fOuterRadius = outerRadius;
1959 verts->fInnerRadius = innerRadius;
1960 verts++;
1961 }
jvanverthc3d0e422016-08-25 08:12:35 -07001962 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001963 // Effectively this is an additional stroked rrect, with its
1964 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1965 // This will give us correct AA in the center and the correct
1966 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001967 //
jvanvertha4f1af82016-08-29 07:17:47 -07001968 // Also, the outer offset is a constant vector pointing to the right, which
1969 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001970 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001971 SkASSERT(args.fInnerRadius <= 0.0f);
1972
jvanvertha4f1af82016-08-29 07:17:47 -07001973 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1974 // this is the normalized distance from the outer rectangle of this
1975 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001976 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001977
Brian Salomon289e3d82016-12-14 15:52:56 -05001978 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1979 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001980 }
jvanverth6a397612016-08-26 08:15:33 -07001981
Robert Phillips79839d42016-10-06 15:03:34 -04001982 if (kFillWithDist_RRectType == args.fType) {
1983 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001984
Robert Phillips79839d42016-10-06 15:03:34 -04001985 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001986
Brian Salomon289e3d82016-12-14 15:52:56 -05001987 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
1988 -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001989 }
jvanverth84839f62016-08-29 10:16:40 -07001990
1991 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1992 const int primIndexCount = rrect_type_to_index_count(args.fType);
1993 for (int i = 0; i < primIndexCount; ++i) {
1994 *indices++ = primIndices[i] + currStartVertex;
1995 }
1996
1997 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001998 }
1999
jvanverth84839f62016-08-29 10:16:40 -07002000 GrMesh mesh;
2001 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
2002 firstIndex, fVertCount, fIndexCount);
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002003 target->draw(gp.get(), this->pipeline(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002004 }
2005
Brian Salomon25a88092016-12-01 09:36:50 -05002006 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002007 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002008
2009 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002010 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002011 return false;
2012 }
2013
bsalomonabd30f52015-08-13 13:34:48 -07002014 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2015 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002016 return false;
2017 }
2018
bsalomoncdaa97b2016-03-08 08:30:14 -08002019 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002020 return false;
2021 }
2022
bsalomoncdaa97b2016-03-08 08:30:14 -08002023 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002024 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002025 fVertCount += that->fVertCount;
2026 fIndexCount += that->fIndexCount;
2027 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002028 return true;
2029 }
2030
bsalomon4b4a7cc2016-07-08 04:42:54 -07002031 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05002032 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002033 SkScalar fInnerRadius;
2034 SkScalar fOuterRadius;
2035 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002036 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002037 };
2038
joshualitt76e7fb62015-02-11 08:52:27 -08002039 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05002040 SkMatrix fViewMatrixIfUsingLocalCoords;
2041 int fVertCount;
2042 int fIndexCount;
2043 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002044
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002045 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002046};
2047
jvanverth84839f62016-08-29 10:16:40 -07002048static const int kNumRRectsInIndexBuffer = 256;
2049
2050GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2051GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2052static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2053 GrResourceProvider* resourceProvider) {
2054 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2055 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2056 switch (type) {
2057 case kFill_RRectType:
2058 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002059 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2060 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002061 case kStroke_RRectType:
2062 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002063 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2064 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002065 default:
2066 SkASSERT(false);
2067 return nullptr;
2068 };
2069}
2070
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002071class EllipticalRRectOp : public GrLegacyMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08002072public:
Brian Salomon25a88092016-12-01 09:36:50 -05002073 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002074
bsalomon4b4a7cc2016-07-08 04:42:54 -07002075 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2076 // whether the rrect is only stroked or stroked and filled.
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002077 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
2078 const SkRect& devRect, float devXRadius,
2079 float devYRadius, SkVector devStrokeWidths,
2080 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002081 SkASSERT(devXRadius > 0.5);
2082 SkASSERT(devYRadius > 0.5);
2083 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2084 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2085 SkScalar innerXRadius = 0.0f;
2086 SkScalar innerYRadius = 0.0f;
2087 SkRect bounds = devRect;
2088 bool stroked = false;
2089 if (devStrokeWidths.fX > 0) {
2090 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2091 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2092 } else {
2093 devStrokeWidths.scale(SK_ScalarHalf);
2094 }
joshualitt76e7fb62015-02-11 08:52:27 -08002095
bsalomon4b4a7cc2016-07-08 04:42:54 -07002096 // we only handle thick strokes for near-circular ellipses
2097 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002098 (SK_ScalarHalf * devXRadius > devYRadius ||
2099 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002100 return nullptr;
2101 }
2102
2103 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002104 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2105 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002106 return nullptr;
2107 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002108 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2109 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002110 return nullptr;
2111 }
2112
2113 // this is legit only if scale & translation (which should be the case at the moment)
2114 if (strokeOnly) {
2115 innerXRadius = devXRadius - devStrokeWidths.fX;
2116 innerYRadius = devYRadius - devStrokeWidths.fY;
2117 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2118 }
2119
2120 devXRadius += devStrokeWidths.fX;
2121 devYRadius += devStrokeWidths.fY;
2122 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2123 }
2124
Brian Salomonf8334782017-01-03 09:42:58 -05002125 std::unique_ptr<EllipticalRRectOp> op(new EllipticalRRectOp());
Brian Salomon289e3d82016-12-14 15:52:56 -05002126 op->fStroked = stroked;
2127 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
2128 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002129 // Expand the rect for aa in order to generate the correct vertices.
2130 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon289e3d82016-12-14 15:52:56 -05002131 op->fGeoData.emplace_back(
2132 Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2133 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08002134 }
2135
Brian Salomon289e3d82016-12-14 15:52:56 -05002136 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002137
Brian Salomon7c3e7182016-12-01 09:35:30 -05002138 SkString dumpInfo() const override {
2139 SkString string;
2140 string.appendf("Stroked: %d\n", fStroked);
2141 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002142 string.appendf(
2143 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2144 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2145 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2146 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2147 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002148 }
2149 string.append(DumpPipelineInfo(*this->pipeline()));
2150 string.append(INHERITED::dumpInfo());
2151 return string;
2152 }
2153
bsalomone46f9fe2015-08-18 06:05:14 -07002154private:
Brian Salomon289e3d82016-12-14 15:52:56 -05002155 EllipticalRRectOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07002156
Brian Salomona811b122017-03-30 08:21:32 -04002157 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
2158 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -04002159 color->setToConstant(fGeoData[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -04002160 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -05002161 }
2162
Brian Salomone7d30482017-03-29 12:09:15 -04002163 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -05002164 optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
2165 if (!optimizations.readsLocalCoords()) {
bsalomoncdaa97b2016-03-08 08:30:14 -08002166 fViewMatrixIfUsingLocalCoords.reset();
2167 }
joshualitt76e7fb62015-02-11 08:52:27 -08002168 }
2169
joshualitt144c3c82015-11-30 12:30:13 -08002170 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002171 SkMatrix localMatrix;
2172 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002173 return;
2174 }
2175
2176 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002177 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002178
joshualitt76e7fb62015-02-11 08:52:27 -08002179 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002180 size_t vertexStride = gp->getVertexStride();
2181 SkASSERT(vertexStride == sizeof(EllipseVertex));
2182
bsalomonb5238a72015-05-05 07:49:49 -07002183 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002184 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002185 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2186 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002187
bsalomonb5238a72015-05-05 07:49:49 -07002188 InstancedHelper helper;
2189 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05002190 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
2191 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002192 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002193 SkDebugf("Could not allocate vertices\n");
2194 return;
2195 }
2196
joshualitt76e7fb62015-02-11 08:52:27 -08002197 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002198 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002199
brianosmanbb2ff942016-02-11 14:15:18 -08002200 GrColor color = args.fColor;
2201
joshualitt76e7fb62015-02-11 08:52:27 -08002202 // Compute the reciprocals of the radii here to save time in the shader
2203 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2204 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2205 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2206 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2207
2208 // Extend the radii out half a pixel to antialias.
2209 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2210 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2211
egdanielbc227142015-04-21 06:28:08 -07002212 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002213
Brian Salomon289e3d82016-12-14 15:52:56 -05002214 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2215 bounds.fBottom - yOuterRadius, bounds.fBottom};
2216 SkScalar yOuterOffsets[4] = {yOuterRadius,
2217 SK_ScalarNearlyZero, // we're using inversesqrt() in
2218 // shader, so can't be exactly 0
2219 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002220
2221 for (int i = 0; i < 4; ++i) {
2222 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002223 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002224 verts->fOffset = SkPoint::Make(xOuterRadius, 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.fLeft + 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 - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002237 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002238 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2239 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2240 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2241 verts++;
2242
2243 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002244 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002245 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2246 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2247 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2248 verts++;
2249 }
2250 }
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002251 helper.recordDraw(target, gp.get(), this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08002252 }
2253
Brian Salomon25a88092016-12-01 09:36:50 -05002254 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002255 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002256
2257 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2258 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002259 return false;
2260 }
2261
bsalomoncdaa97b2016-03-08 08:30:14 -08002262 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002263 return false;
2264 }
2265
bsalomoncdaa97b2016-03-08 08:30:14 -08002266 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002267 return false;
2268 }
2269
bsalomoncdaa97b2016-03-08 08:30:14 -08002270 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002271 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002272 return true;
2273 }
2274
bsalomon4b4a7cc2016-07-08 04:42:54 -07002275 struct Geometry {
2276 GrColor fColor;
2277 SkScalar fXRadius;
2278 SkScalar fYRadius;
2279 SkScalar fInnerXRadius;
2280 SkScalar fInnerYRadius;
2281 SkRect fDevBounds;
2282 };
2283
Brian Salomon289e3d82016-12-14 15:52:56 -05002284 bool fStroked;
2285 SkMatrix fViewMatrixIfUsingLocalCoords;
2286 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002287
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002288 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002289};
2290
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002291static std::unique_ptr<GrLegacyMeshDrawOp> make_rrect_op(GrColor color,
2292 bool needsDistance,
2293 const SkMatrix& viewMatrix,
2294 const SkRRect& rrect,
2295 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002296 SkASSERT(viewMatrix.rectStaysRect());
2297 SkASSERT(rrect.isSimple());
2298 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002299
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002300 // RRect ops only handle simple, but not too simple, rrects.
2301 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002302 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002303 SkRect bounds;
2304 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002305
2306 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002307 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2308 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2309 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2310 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002311
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002312 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002313
bsalomon4b4a7cc2016-07-08 04:42:54 -07002314 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2315 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002316 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002317
Brian Salomon289e3d82016-12-14 15:52:56 -05002318 bool isStrokeOnly =
2319 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002320 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2321
jvanverthc3d0e422016-08-25 08:12:35 -07002322 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002323 if (hasStroke) {
2324 if (SkStrokeRec::kHairline_Style == style) {
2325 scaledStroke.set(1, 1);
2326 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002327 scaledStroke.fX = SkScalarAbs(
2328 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2329 scaledStroke.fY = SkScalarAbs(
2330 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002331 }
2332
jvanverthc3d0e422016-08-25 08:12:35 -07002333 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2334 // for non-circular rrects, if half of strokewidth is greater than radius,
2335 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002336 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2337 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002338 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002339 }
2340 }
2341
2342 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2343 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2344 // patch will have fractional coverage. This only matters when the interior is actually filled.
2345 // We could consider falling back to rect rendering here, since a tiny radius is
2346 // indistinguishable from a square corner.
2347 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002348 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002349 }
2350
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002351 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002352 if (isCircular) {
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002353 return std::unique_ptr<GrLegacyMeshDrawOp>(new CircularRRectOp(
Brian Salomonf8334782017-01-03 09:42:58 -05002354 color, needsDistance, viewMatrix, bounds, xRadius, scaledStroke.fX, isStrokeOnly));
Brian Salomon289e3d82016-12-14 15:52:56 -05002355 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002356 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002357 return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
2358 isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002359 }
joshualitt3e708c52015-04-30 13:49:27 -07002360}
2361
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002362std::unique_ptr<GrLegacyMeshDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
2363 bool needsDistance,
2364 const SkMatrix& viewMatrix,
2365 const SkRRect& rrect,
2366 const SkStrokeRec& stroke,
2367 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002368 if (rrect.isOval()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002369 return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002370 }
2371
2372 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002373 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002374 }
2375
Brian Salomon289e3d82016-12-14 15:52:56 -05002376 return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002377}
joshualitt3e708c52015-04-30 13:49:27 -07002378
bsalomon4b4a7cc2016-07-08 04:42:54 -07002379///////////////////////////////////////////////////////////////////////////////
2380
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002381std::unique_ptr<GrLegacyMeshDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
2382 const SkMatrix& viewMatrix,
2383 const SkRect& oval,
2384 const SkStrokeRec& stroke,
2385 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002386 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002387 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002388 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2389 circle_stays_circle(viewMatrix)) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002390 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002391 return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002392 }
2393
Stan Ilieveb868aa2017-02-21 11:06:16 -05002394 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002395 if (viewMatrix.rectStaysRect()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002396 return EllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002397 }
2398
Stan Ilieveb868aa2017-02-21 11:06:16 -05002399 // Otherwise, if we have shader derivative support, render as device-independent
2400 if (shaderCaps->shaderDerivativeSupport()) {
2401 return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
2402 }
2403
bsalomon4b4a7cc2016-07-08 04:42:54 -07002404 return nullptr;
2405}
2406
2407///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002408
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002409std::unique_ptr<GrLegacyMeshDrawOp> GrOvalOpFactory::MakeArcOp(
2410 GrColor color, const SkMatrix& viewMatrix, const SkRect& oval, SkScalar startAngle,
2411 SkScalar sweepAngle, bool useCenter, const GrStyle& style, const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002412 SkASSERT(!oval.isEmpty());
2413 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002414 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002415 if (SkScalarAbs(sweepAngle) >= 360.f) {
2416 return nullptr;
2417 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002418 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2419 return nullptr;
2420 }
2421 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002422 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2423 useCenter};
2424 return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002425}
2426
2427///////////////////////////////////////////////////////////////////////////////
2428
Hal Canary6f6961e2017-01-31 13:50:44 -05002429#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002430
Brian Salomon5ec9def2016-12-20 15:34:05 -05002431DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002432 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002433 SkScalar rotate = random->nextSScalar1() * 360.f;
2434 SkScalar translateX = random->nextSScalar1() * 1000.f;
2435 SkScalar translateY = random->nextSScalar1() * 1000.f;
2436 SkScalar scale = random->nextSScalar1() * 100.f;
2437 SkMatrix viewMatrix;
2438 viewMatrix.setRotate(rotate);
2439 viewMatrix.postTranslate(translateX, translateY);
2440 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002441 GrColor color = GrRandomColor(random);
2442 SkRect circle = GrTest::TestSquare(random);
2443 SkPoint center = {circle.centerX(), circle.centerY()};
2444 SkScalar radius = circle.width() / 2.f;
2445 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002446 CircleOp::ArcParams arcParamsTmp;
2447 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002448 if (random->nextBool()) {
2449 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002450 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2451 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002452 arcParams = &arcParamsTmp;
2453 }
Brian Salomond3ccb0a2017-04-03 10:38:00 -04002454 std::unique_ptr<GrLegacyMeshDrawOp> op = CircleOp::Make(
2455 color, viewMatrix, center, radius, GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002456 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002457 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002458 }
2459 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002460}
2461
Brian Salomon5ec9def2016-12-20 15:34:05 -05002462DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002463 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2464 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002465 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002466 return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002467}
2468
Brian Salomon5ec9def2016-12-20 15:34:05 -05002469DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002470 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2471 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002472 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon5ec9def2016-12-20 15:34:05 -05002473 return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002474}
2475
Brian Salomon5ec9def2016-12-20 15:34:05 -05002476DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002477 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2478 GrColor color = GrRandomColor(random);
2479 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002480 bool needsDistance = random->nextBool();
Brian Salomon5ec9def2016-12-20 15:34:05 -05002481 return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002482}
2483
2484#endif