blob: 623ec7ab7f58cda86d72ee5d5b53f15e2665884a [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Brian Salomon289e3d82016-12-14 15:52:56 -05008#include "GrOvalOpFactory.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -05009#include "GrDrawOpTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080010#include "GrGeometryProcessor.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050011#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080012#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070013#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050014#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070015#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
egdaniel2d721d32015-11-11 13:06:05 -080018#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080019#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070020#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080021#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080022#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050023#include "glsl/GrGLSLVarying.h"
24#include "glsl/GrGLSLVertexShaderBuilder.h"
Brian Salomon89527432016-12-16 09:52:16 -050025#include "ops/GrMeshDrawOp.h"
Brian Salomon05441c42017-05-15 16:45:49 -040026#include "ops/GrSimpleMeshDrawOpHelper.h"
joshualitt76e7fb62015-02-11 08:52:27 -080027
commit-bot@chromium.org81312832013-03-22 18:34:09 +000028namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080029
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050031 SkPoint fPos;
32 GrColor fColor;
33 SkPoint fOffset;
34 SkPoint fOuterRadii;
35 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000036};
37
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000038struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050039 SkPoint fPos;
40 GrColor fColor;
41 SkPoint fOuterOffset;
42 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000043};
44
Brian Salomon289e3d82016-12-14 15:52:56 -050045static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000046}
47
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000048///////////////////////////////////////////////////////////////////////////////
49
50/**
bsalomonce1c8862014-12-15 07:11:22 -080051 * The output of this effect is a modulation of the input color and coverage for a circle. It
52 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080053 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080054 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080055 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080056 * vec4f : (p.xy, outerRad, innerRad)
57 * p is the position in the normalized space.
58 * outerRad is the outerRadius in device space.
59 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040060 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070061 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
62 * vec4f : (v.xy, outerDistance, innerDistance)
63 * v is a normalized vector pointing to the outer edge
64 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
65 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070066 * Additional clip planes are supported for rendering circular arcs. The additional planes are
67 * either intersected or unioned together. Up to three planes are supported (an initial plane,
68 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
Brian Salomon53e4c3c2016-12-21 11:38:53 -050069 * are useful for any given arc, but having all three in one instance allows combining different
bsalomon4f3a0ca2016-08-22 13:14:26 -070070 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000071 */
72
bsalomoncdaa97b2016-03-08 08:30:14 -080073class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070075 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
76 const SkMatrix& localMatrix)
77 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080078 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070079 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
80 kHigh_GrSLPrecision);
81 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
Jim Van Verth6750e912016-12-19 14:45:19 -050082 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
83 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -070084 if (clipPlane) {
85 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
86 } else {
87 fInClipPlane = nullptr;
88 }
89 if (isectPlane) {
90 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
91 } else {
92 fInIsectPlane = nullptr;
93 }
94 if (unionPlane) {
95 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
96 } else {
97 fInUnionPlane = nullptr;
98 }
bsalomoncdaa97b2016-03-08 08:30:14 -080099 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000100 }
101
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400102 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700103
Brian Salomond3b65972017-03-22 12:05:03 -0400104 ~CircleGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105
mtklein36352bf2015-03-25 18:17:31 -0700106 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
Brian Salomon94efbf52016-11-29 13:43:05 -0500108 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700109 GLSLProcessor::GenKey(*this, caps, b);
110 }
111
Brian Salomon94efbf52016-11-29 13:43:05 -0500112 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700113 return new GLSLProcessor();
114 }
115
116private:
egdaniel57d3b032015-11-13 11:57:27 -0800117 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000118 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800119 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000120
Brian Salomon289e3d82016-12-14 15:52:56 -0500121 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800122 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800123 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800124 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800125 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700126 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800127
joshualittabb52a12015-01-13 15:02:10 -0800128 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800129 varyingHandler->emitAttributes(cgp);
Jim Van Verth6750e912016-12-19 14:45:19 -0500130 fragBuilder->codeAppend("highp vec4 circleEdge;");
131 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
132 kHigh_GrSLPrecision);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700133 if (cgp.fInClipPlane) {
134 fragBuilder->codeAppend("vec3 clipPlane;");
135 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
136 }
137 if (cgp.fInIsectPlane) {
138 SkASSERT(cgp.fInClipPlane);
139 fragBuilder->codeAppend("vec3 isectPlane;");
140 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
141 }
142 if (cgp.fInUnionPlane) {
143 SkASSERT(cgp.fInClipPlane);
144 fragBuilder->codeAppend("vec3 unionPlane;");
145 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
146 }
joshualittabb52a12015-01-13 15:02:10 -0800147
joshualittb8c241a2015-05-19 08:23:30 -0700148 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700149 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800150
joshualittabb52a12015-01-13 15:02:10 -0800151 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700152 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800153
154 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800155 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800156 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800157 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800158 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700159 cgp.fInPosition->fName,
160 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700161 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800162
Jim Van Verth6750e912016-12-19 14:45:19 -0500163 fragBuilder->codeAppend("highp float d = length(circleEdge.xy);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700164 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
165 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800166 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500167 fragBuilder->codeAppend(
168 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700169 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800170 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000172
dvonbeck68f2f7d2016-08-01 11:37:45 -0700173 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700174 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
Brian Salomon289e3d82016-12-14 15:52:56 -0500175 fragBuilder->codeAppendf(
176 "if (d == 0.0) {" // if on the center of the circle
177 " %s = vec4(1.0, 0.0, distanceToOuterEdge, "
178 " %s);", // no normalize
179 args.fDistanceVectorName,
180 innerEdgeDistance);
181 fragBuilder->codeAppendf(
182 "} else {"
183 " %s = vec4(normalize(circleEdge.xy),"
184 " distanceToOuterEdge, %s);"
185 "}",
186 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700187 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700188 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500189 fragBuilder->codeAppend(
190 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
191 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700192 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500193 fragBuilder->codeAppend(
194 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
195 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700196 }
197 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500198 fragBuilder->codeAppend(
199 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
200 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700201 }
202 fragBuilder->codeAppend("edgeAlpha *= clip;");
203 }
egdaniel4ca2e602015-11-18 08:01:26 -0800204 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205 }
206
robertphillips46d36f02015-01-18 08:14:14 -0800207 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500208 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700209 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800210 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700211 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500212 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500214 key |= cgp.fInClipPlane ? 0x04 : 0x0;
215 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
216 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700217 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218 }
219
bsalomona624bf32016-09-20 09:12:47 -0700220 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
221 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700222 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700223 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700224 }
225
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 private:
egdaniele659a582015-11-13 09:55:43 -0800227 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 };
229
Brian Salomon289e3d82016-12-14 15:52:56 -0500230 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800231 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800232 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800233 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700234 const Attribute* fInClipPlane;
235 const Attribute* fInIsectPlane;
236 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500237 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238
joshualittb0a8a372014-09-23 09:50:21 -0700239 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000240
joshualitt249af152014-09-15 11:41:13 -0700241 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242};
243
bsalomoncdaa97b2016-03-08 08:30:14 -0800244GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245
Hal Canary6f6961e2017-01-31 13:50:44 -0500246#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700247sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500248 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
249 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
250 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251}
Hal Canary6f6961e2017-01-31 13:50:44 -0500252#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253
254///////////////////////////////////////////////////////////////////////////////
255
256/**
257 * 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 +0000258 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
259 * in both x and y directions.
260 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000261 * 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 +0000262 */
263
bsalomoncdaa97b2016-03-08 08:30:14 -0800264class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500266 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800267 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700268 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
269 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
270 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
271 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800272 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273 }
274
Brian Salomond3b65972017-03-22 12:05:03 -0400275 ~EllipseGeometryProcessor() override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000276
mtklein36352bf2015-03-25 18:17:31 -0700277 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800278
Brian Salomon94efbf52016-11-29 13:43:05 -0500279 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700280 GLSLProcessor::GenKey(*this, caps, b);
281 }
282
Brian Salomon94efbf52016-11-29 13:43:05 -0500283 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700284 return new GLSLProcessor();
285 }
286
287private:
egdaniel57d3b032015-11-13 11:57:27 -0800288 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000289 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800290 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000291
Brian Salomon289e3d82016-12-14 15:52:56 -0500292 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800293 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800294 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800295 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800296 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000297
joshualittabb52a12015-01-13 15:02:10 -0800298 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800299 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800300
egdaniel8dcdedc2015-11-11 06:27:20 -0800301 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800302 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800303 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700304 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000305
egdaniel8dcdedc2015-11-11 06:27:20 -0800306 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800307 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500308 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800309
cdalton85285412016-02-18 12:37:07 -0800310 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700311 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700312 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800313
joshualittabb52a12015-01-13 15:02:10 -0800314 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700315 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800316
317 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800318 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800319 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800320 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800321 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700322 egp.fInPosition->fName,
323 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700324 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800325
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000326 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800327 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
328 ellipseRadii.fsIn());
329 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
330 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
331 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700332
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000333 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800334 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
335 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800336 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000337
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000338 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800339 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500340 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800341 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500342 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
343 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800344 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
345 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 }
347
egdaniel4ca2e602015-11-18 08:01:26 -0800348 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000349 }
350
robertphillips46d36f02015-01-18 08:14:14 -0800351 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500352 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700353 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800354 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
355 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700356 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700357 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000358 }
359
bsalomona624bf32016-09-20 09:12:47 -0700360 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
361 FPCoordTransformIter&& transformIter) override {
362 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
363 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700364 }
365
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000366 private:
egdaniele659a582015-11-13 09:55:43 -0800367 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000368 };
369
joshualitt71c92602015-01-14 08:12:47 -0800370 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800371 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800372 const Attribute* fInEllipseOffset;
373 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700374 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000375 bool fStroke;
376
joshualittb0a8a372014-09-23 09:50:21 -0700377 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000378
joshualitt249af152014-09-15 11:41:13 -0700379 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380};
381
bsalomoncdaa97b2016-03-08 08:30:14 -0800382GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000383
Hal Canary6f6961e2017-01-31 13:50:44 -0500384#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700385sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
386 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500387 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000388}
Hal Canary6f6961e2017-01-31 13:50:44 -0500389#endif
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000390
391///////////////////////////////////////////////////////////////////////////////
392
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000393/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000394 * 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 +0000395 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
396 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
397 * using differentials.
398 *
399 * The result is device-independent and can be used with any affine matrix.
400 */
401
bsalomoncdaa97b2016-03-08 08:30:14 -0800402enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000403
bsalomoncdaa97b2016-03-08 08:30:14 -0800404class DIEllipseGeometryProcessor : public GrGeometryProcessor {
405public:
406 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500407 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800408 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700409 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
410 kHigh_GrSLPrecision);
411 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
412 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
413 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800414 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000415 }
416
Brian Salomond3b65972017-03-22 12:05:03 -0400417 ~DIEllipseGeometryProcessor() override {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000418
mtklein36352bf2015-03-25 18:17:31 -0700419 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000420
Brian Salomon94efbf52016-11-29 13:43:05 -0500421 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700422 GLSLProcessor::GenKey(*this, caps, b);
423 }
halcanary9d524f22016-03-29 09:03:52 -0700424
Brian Salomon94efbf52016-11-29 13:43:05 -0500425 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700426 return new GLSLProcessor();
427 }
428
429private:
egdaniel57d3b032015-11-13 11:57:27 -0800430 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000431 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500432 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433
joshualitt465283c2015-09-11 08:19:35 -0700434 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800435 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800436 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800437 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800438 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000439
joshualittabb52a12015-01-13 15:02:10 -0800440 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800441 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800442
egdaniel8dcdedc2015-11-11 06:27:20 -0800443 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800444 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500445 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700446
egdaniel8dcdedc2015-11-11 06:27:20 -0800447 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800448 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500449 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800450
cdalton85285412016-02-18 12:37:07 -0800451 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700452 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800453
joshualittabb52a12015-01-13 15:02:10 -0800454 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800455 this->setupPosition(vertBuilder,
456 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800457 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700458 diegp.fInPosition->fName,
459 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700460 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800461
462 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800463 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800464 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800465 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800466 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700467 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700468 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800469
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000470 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800471 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
472 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
473 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
474 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500475 fragBuilder->codeAppendf(
476 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
477 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
478 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000479
egdaniel4ca2e602015-11-18 08:01:26 -0800480 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000481 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800482 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
483 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800484 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000485 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800486 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
487 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800489 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000490 }
491
492 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800493 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800494 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
495 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
496 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
497 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500498 fragBuilder->codeAppendf(
499 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
500 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
501 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800502 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
503 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000504 }
505
egdaniel4ca2e602015-11-18 08:01:26 -0800506 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000507 }
508
robertphillips46d36f02015-01-18 08:14:14 -0800509 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500510 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700511 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800512 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
513 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700514 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700515 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000516 }
517
bsalomona624bf32016-09-20 09:12:47 -0700518 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
519 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800520 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700521
bsalomon31df31c2016-08-17 09:00:24 -0700522 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
523 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700524 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800525 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700526 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
527 }
bsalomona624bf32016-09-20 09:12:47 -0700528 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000529 }
530
531 private:
joshualitt5559ca22015-05-21 15:50:36 -0700532 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700533 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800534
egdaniele659a582015-11-13 09:55:43 -0800535 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000536 };
537
joshualitt71c92602015-01-14 08:12:47 -0800538 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800539 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800540 const Attribute* fInEllipseOffsets0;
541 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500542 SkMatrix fViewMatrix;
543 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000544
joshualittb0a8a372014-09-23 09:50:21 -0700545 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546
joshualitt249af152014-09-15 11:41:13 -0700547 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548};
549
bsalomoncdaa97b2016-03-08 08:30:14 -0800550GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000551
Hal Canary6f6961e2017-01-31 13:50:44 -0500552#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -0700553sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500554 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
555 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000556}
Hal Canary6f6961e2017-01-31 13:50:44 -0500557#endif
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000558
559///////////////////////////////////////////////////////////////////////////////
560
jvanverth6ca48822016-10-07 06:57:32 -0700561// We have two possible cases for geometry for a circle:
562
563// In the case of a normal fill, we draw geometry for the circle as an octagon.
564static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500565 // enter the octagon
566 // clang-format off
567 0, 1, 8, 1, 2, 8,
568 2, 3, 8, 3, 4, 8,
569 4, 5, 8, 5, 6, 8,
570 6, 7, 8, 7, 0, 8
571 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700572};
573
574// For stroked circles, we use two nested octagons.
575static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500576 // enter the octagon
577 // clang-format off
578 0, 1, 9, 0, 9, 8,
579 1, 2, 10, 1, 10, 9,
580 2, 3, 11, 2, 11, 10,
581 3, 4, 12, 3, 12, 11,
582 4, 5, 13, 4, 13, 12,
583 5, 6, 14, 5, 14, 13,
584 6, 7, 15, 6, 15, 14,
585 7, 0, 8, 7, 8, 15,
586 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700587};
588
Brian Salomon289e3d82016-12-14 15:52:56 -0500589
jvanverth6ca48822016-10-07 06:57:32 -0700590static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
591static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
592static const int kVertsPerStrokeCircle = 16;
593static const int kVertsPerFillCircle = 9;
594
595static int circle_type_to_vert_count(bool stroked) {
596 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
597}
598
599static int circle_type_to_index_count(bool stroked) {
600 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
601}
602
603static const uint16_t* circle_type_to_indices(bool stroked) {
604 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
605}
606
607///////////////////////////////////////////////////////////////////////////////
608
Brian Salomon05441c42017-05-15 16:45:49 -0400609class CircleOp final : public GrMeshDrawOp {
610private:
611 using Helper = GrSimpleMeshDrawOpHelper;
612
joshualitt76e7fb62015-02-11 08:52:27 -0800613public:
Brian Salomon25a88092016-12-01 09:36:50 -0500614 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700615
bsalomon4f3a0ca2016-08-22 13:14:26 -0700616 /** Optional extra params to render a partial arc rather than a full circle. */
617 struct ArcParams {
618 SkScalar fStartAngleRadians;
619 SkScalar fSweepAngleRadians;
620 bool fUseCenter;
621 };
Brian Salomon05441c42017-05-15 16:45:49 -0400622
623 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
624 SkPoint center, SkScalar radius, const GrStyle& style,
625 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700626 SkASSERT(circle_stays_circle(viewMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700627 if (style.hasPathEffect()) {
628 return nullptr;
629 }
Brian Salomon05441c42017-05-15 16:45:49 -0400630 const SkStrokeRec& stroke = style.strokeRec();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700631 SkStrokeRec::Style recStyle = stroke.getStyle();
632 if (arcParams) {
633 // Arc support depends on the style.
634 switch (recStyle) {
635 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500636 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700637 return nullptr;
638 case SkStrokeRec::kFill_Style:
639 // This supports all fills.
640 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500641 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700642 case SkStrokeRec::kHairline_Style:
643 // Strokes that don't use the center point are supported with butt cap.
644 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
645 return nullptr;
646 }
647 break;
648 }
649 }
Brian Salomon05441c42017-05-15 16:45:49 -0400650 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
651 arcParams);
652 }
653
654 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
655 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
656 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
657 const SkStrokeRec& stroke = style.strokeRec();
658 SkStrokeRec::Style recStyle = stroke.getStyle();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700659
bsalomon4b4a7cc2016-07-08 04:42:54 -0700660 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700661 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700662 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800663
Brian Salomon289e3d82016-12-14 15:52:56 -0500664 bool isStrokeOnly =
665 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700666 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700667
jvanverth6ca48822016-10-07 06:57:32 -0700668 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700669 SkScalar outerRadius = radius;
670 SkScalar halfWidth = 0;
671 if (hasStroke) {
672 if (SkScalarNearlyZero(strokeWidth)) {
673 halfWidth = SK_ScalarHalf;
674 } else {
675 halfWidth = SkScalarHalf(strokeWidth);
676 }
677
678 outerRadius += halfWidth;
679 if (isStrokeOnly) {
680 innerRadius = radius - halfWidth;
681 }
682 }
683
684 // The radii are outset for two reasons. First, it allows the shader to simply perform
685 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
686 // Second, the outer radius is used to compute the verts of the bounding box that is
687 // rendered and the outset ensures the box will cover all partially covered by the circle.
688 outerRadius += SK_ScalarHalf;
689 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700690 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon05441c42017-05-15 16:45:49 -0400691 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700692
bsalomon4f3a0ca2016-08-22 13:14:26 -0700693 // This makes every point fully inside the intersection plane.
694 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
695 // This makes every point fully outside the union plane.
696 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
697 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
698 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700699 if (arcParams) {
700 // The shader operates in a space where the circle is translated to be centered at the
701 // origin. Here we compute points on the unit circle at the starting and ending angles.
702 SkPoint startPoint, stopPoint;
703 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
704 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
705 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
Brian Osmanfd773fb2017-05-17 11:43:11 -0400706
707 // Adjust the start and end points based on the view matrix (to handle rotated arcs)
708 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
709 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
710 startPoint.normalize();
711 stopPoint.normalize();
712
713 // If the matrix included scale (on one axis) we need to swap our start and end points
714 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
715 SkTSwap(startPoint, stopPoint);
716 }
717
bsalomon4f3a0ca2016-08-22 13:14:26 -0700718 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
719 // radial lines. However, in both cases we have to be careful about the half-circle.
720 // case. In that case the two radial lines are equal and so that edge gets clipped
721 // twice. Since the shared edge goes through the center we fall back on the useCenter
722 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500723 bool useCenter =
724 (arcParams->fUseCenter || isStrokeOnly) &&
725 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 if (useCenter) {
727 SkVector norm0 = {startPoint.fY, -startPoint.fX};
728 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
729 if (arcParams->fSweepAngleRadians > 0) {
730 norm0.negate();
731 } else {
732 norm1.negate();
733 }
Brian Salomon05441c42017-05-15 16:45:49 -0400734 fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700735 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon05441c42017-05-15 16:45:49 -0400736 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700737 color,
738 innerRadius,
739 outerRadius,
740 {norm0.fX, norm0.fY, 0.5f},
741 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
742 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700743 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500744 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400745 fClipPlaneIsect = false;
746 fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700747 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400748 fCircles.emplace_back(Circle{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700749 color,
750 innerRadius,
751 outerRadius,
752 {norm0.fX, norm0.fY, 0.5f},
753 {norm1.fX, norm1.fY, 0.5f},
754 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700755 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500756 stroked});
Brian Salomon05441c42017-05-15 16:45:49 -0400757 fClipPlaneIsect = true;
758 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700759 }
760 } else {
761 // We clip to a secant of the original circle.
762 startPoint.scale(radius);
763 stopPoint.scale(radius);
764 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
765 norm.normalize();
766 if (arcParams->fSweepAngleRadians > 0) {
767 norm.negate();
768 }
769 SkScalar d = -norm.dot(startPoint) + 0.5f;
770
Brian Salomon05441c42017-05-15 16:45:49 -0400771 fCircles.emplace_back(
772 Circle{color,
773 innerRadius,
774 outerRadius,
775 {norm.fX, norm.fY, d},
776 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
777 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
778 devBounds,
779 stroked});
780 fClipPlane = true;
781 fClipPlaneIsect = false;
782 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700783 }
784 } else {
Brian Salomon05441c42017-05-15 16:45:49 -0400785 fCircles.emplace_back(
786 Circle{color,
787 innerRadius,
788 outerRadius,
789 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
790 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
791 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
792 devBounds,
793 stroked});
794 fClipPlane = false;
795 fClipPlaneIsect = false;
796 fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700797 }
bsalomon88cf17d2016-07-08 06:40:56 -0700798 // Use the original radius and stroke radius for the bounds so that it does not include the
799 // AA bloat.
800 radius += halfWidth;
Brian Salomon05441c42017-05-15 16:45:49 -0400801 this->setBounds(
Brian Salomon289e3d82016-12-14 15:52:56 -0500802 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
803 HasAABloat::kYes, IsZeroArea::kNo);
Brian Salomon05441c42017-05-15 16:45:49 -0400804 fVertCount = circle_type_to_vert_count(stroked);
805 fIndexCount = circle_type_to_index_count(stroked);
806 fAllFill = !stroked;
bsalomoncdaa97b2016-03-08 08:30:14 -0800807 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700808
Brian Salomon289e3d82016-12-14 15:52:56 -0500809 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800810
robertphillipse004bfc2015-11-16 09:06:59 -0800811 SkString dumpInfo() const override {
812 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -0400813 for (int i = 0; i < fCircles.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500814 string.appendf(
815 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
816 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -0400817 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
818 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
819 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800820 }
821 string.append(INHERITED::dumpInfo());
822 return string;
823 }
824
Brian Salomon05441c42017-05-15 16:45:49 -0400825 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
826 GrColor* color = &fCircles.front().fColor;
827 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
828 color);
829 }
830
831 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
832
bsalomone46f9fe2015-08-18 06:05:14 -0700833private:
joshualitt144c3c82015-11-30 12:30:13 -0800834 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800835 SkMatrix localMatrix;
836 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800837 return;
838 }
839
840 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500841 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
842 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700843
844 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500845 SkPoint fPos;
846 GrColor fColor;
847 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700848 SkScalar fOuterRadius;
849 SkScalar fInnerRadius;
850 // These planes may or may not be present in the vertex buffer.
851 SkScalar fHalfPlanes[3][3];
852 };
joshualitt76e7fb62015-02-11 08:52:27 -0800853
joshualitt76e7fb62015-02-11 08:52:27 -0800854 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500855 SkASSERT(vertexStride ==
856 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
857 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
858 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700859
860 const GrBuffer* vertexBuffer;
861 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500862 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
863 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700864 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700865 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800866 return;
867 }
868
jvanverth6ca48822016-10-07 06:57:32 -0700869 const GrBuffer* indexBuffer = nullptr;
870 int firstIndex = 0;
871 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
872 if (!indices) {
873 SkDebugf("Could not allocate indices\n");
874 return;
875 }
876
877 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -0400878 for (const auto& circle : fCircles) {
879 SkScalar innerRadius = circle.fInnerRadius;
880 SkScalar outerRadius = circle.fOuterRadius;
881 GrColor color = circle.fColor;
882 const SkRect& bounds = circle.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800883
Brian Salomon289e3d82016-12-14 15:52:56 -0500884 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
885 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
886 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
887 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
888 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
889 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
890 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
891 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800892
893 // The inner radius in the vertex data must be specified in normalized space.
894 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700895
896 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500897 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700898 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500899
Brian Salomon289e3d82016-12-14 15:52:56 -0500900 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700901 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700902 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700903 v0->fOuterRadius = outerRadius;
904 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800905
Brian Salomon289e3d82016-12-14 15:52:56 -0500906 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700907 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700908 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700909 v1->fOuterRadius = outerRadius;
910 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800911
Brian Salomon289e3d82016-12-14 15:52:56 -0500912 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700913 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700914 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700915 v2->fOuterRadius = outerRadius;
916 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800917
Brian Salomon289e3d82016-12-14 15:52:56 -0500918 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700919 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700920 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700921 v3->fOuterRadius = outerRadius;
922 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800923
Brian Salomon289e3d82016-12-14 15:52:56 -0500924 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700925 v4->fColor = color;
926 v4->fOffset = SkPoint::Make(octOffset, 1);
927 v4->fOuterRadius = outerRadius;
928 v4->fInnerRadius = innerRadius;
929
Brian Salomon289e3d82016-12-14 15:52:56 -0500930 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700931 v5->fColor = color;
932 v5->fOffset = SkPoint::Make(-octOffset, 1);
933 v5->fOuterRadius = outerRadius;
934 v5->fInnerRadius = innerRadius;
935
Brian Salomon289e3d82016-12-14 15:52:56 -0500936 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700937 v6->fColor = color;
938 v6->fOffset = SkPoint::Make(-1, octOffset);
939 v6->fOuterRadius = outerRadius;
940 v6->fInnerRadius = innerRadius;
941
Brian Salomon289e3d82016-12-14 15:52:56 -0500942 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700943 v7->fColor = color;
944 v7->fOffset = SkPoint::Make(-1, -octOffset);
945 v7->fOuterRadius = outerRadius;
946 v7->fInnerRadius = innerRadius;
947
bsalomon4f3a0ca2016-08-22 13:14:26 -0700948 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -0400949 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
950 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
951 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
952 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
953 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
954 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
955 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
956 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700957 }
958 int unionIdx = 1;
959 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -0400960 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
961 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
962 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
963 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
964 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
965 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
966 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
967 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700968 unionIdx = 2;
969 }
970 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -0400971 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
972 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
973 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
974 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
975 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
976 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
977 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
978 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700979 }
jvanverth6ca48822016-10-07 06:57:32 -0700980
Brian Salomon05441c42017-05-15 16:45:49 -0400981 if (circle.fStroked) {
jvanverth6ca48822016-10-07 06:57:32 -0700982 // compute the inner ring
983 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
984 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
985 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
986 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
987 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
988 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
989 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
990 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
991
992 // cosine and sine of pi/8
993 SkScalar c = 0.923579533f;
994 SkScalar s = 0.382683432f;
Brian Salomon05441c42017-05-15 16:45:49 -0400995 SkScalar r = circle.fInnerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700996
Brian Salomon289e3d82016-12-14 15:52:56 -0500997 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700998 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500999 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001000 v0->fOuterRadius = outerRadius;
1001 v0->fInnerRadius = innerRadius;
1002
Brian Salomon289e3d82016-12-14 15:52:56 -05001003 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001004 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001005 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001006 v1->fOuterRadius = outerRadius;
1007 v1->fInnerRadius = innerRadius;
1008
Brian Salomon289e3d82016-12-14 15:52:56 -05001009 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001010 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001011 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001012 v2->fOuterRadius = outerRadius;
1013 v2->fInnerRadius = innerRadius;
1014
Brian Salomon289e3d82016-12-14 15:52:56 -05001015 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001016 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001017 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001018 v3->fOuterRadius = outerRadius;
1019 v3->fInnerRadius = innerRadius;
1020
Brian Salomon289e3d82016-12-14 15:52:56 -05001021 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001022 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001023 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001024 v4->fOuterRadius = outerRadius;
1025 v4->fInnerRadius = innerRadius;
1026
Brian Salomon289e3d82016-12-14 15:52:56 -05001027 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001028 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001029 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001030 v5->fOuterRadius = outerRadius;
1031 v5->fInnerRadius = innerRadius;
1032
Brian Salomon289e3d82016-12-14 15:52:56 -05001033 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001034 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001035 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001036 v6->fOuterRadius = outerRadius;
1037 v6->fInnerRadius = innerRadius;
1038
Brian Salomon289e3d82016-12-14 15:52:56 -05001039 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001040 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001041 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001042 v7->fOuterRadius = outerRadius;
1043 v7->fInnerRadius = innerRadius;
1044
1045 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001046 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1047 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1048 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1049 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1050 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1051 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1052 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1053 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001054 }
1055 int unionIdx = 1;
1056 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001057 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1058 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1059 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1060 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1061 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1062 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1063 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1064 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001065 unionIdx = 2;
1066 }
1067 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001068 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1069 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1070 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1071 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1072 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1073 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1074 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1075 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001076 }
1077 } else {
1078 // filled
1079 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1080 v8->fPos = center;
1081 v8->fColor = color;
1082 v8->fOffset = SkPoint::Make(0, 0);
1083 v8->fOuterRadius = outerRadius;
1084 v8->fInnerRadius = innerRadius;
1085 if (fClipPlane) {
Brian Salomon05441c42017-05-15 16:45:49 -04001086 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001087 }
1088 int unionIdx = 1;
1089 if (fClipPlaneIsect) {
Brian Salomon05441c42017-05-15 16:45:49 -04001090 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001091 unionIdx = 2;
1092 }
1093 if (fClipPlaneUnion) {
Brian Salomon05441c42017-05-15 16:45:49 -04001094 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -07001095 }
1096 }
1097
Brian Salomon05441c42017-05-15 16:45:49 -04001098 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1099 const int primIndexCount = circle_type_to_index_count(circle.fStroked);
jvanverth6ca48822016-10-07 06:57:32 -07001100 for (int i = 0; i < primIndexCount; ++i) {
1101 *indices++ = primIndices[i] + currStartVertex;
1102 }
1103
Brian Salomon05441c42017-05-15 16:45:49 -04001104 currStartVertex += circle_type_to_vert_count(circle.fStroked);
1105 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001106 }
jvanverth6ca48822016-10-07 06:57:32 -07001107
Chris Daltonbca46e22017-05-15 11:03:26 -06001108 GrMesh mesh(kTriangles_GrPrimitiveType);
1109 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex);
1110 mesh.setVertices(vertexBuffer, fVertCount, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04001111 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001112 }
1113
Brian Salomon25a88092016-12-01 09:36:50 -05001114 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001115 CircleOp* that = t->cast<CircleOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05001116
1117 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05001118 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05001119 return false;
1120 }
1121
Brian Salomon05441c42017-05-15 16:45:49 -04001122 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001123 return false;
1124 }
1125
Brian Salomon05441c42017-05-15 16:45:49 -04001126 if (fHelper.usesLocalCoords() &&
1127 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001128 return false;
1129 }
1130
Brian Salomon289e3d82016-12-14 15:52:56 -05001131 // Because we've set up the ops that don't use the planes with noop values
1132 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001133 fClipPlane |= that->fClipPlane;
1134 fClipPlaneIsect |= that->fClipPlaneIsect;
1135 fClipPlaneUnion |= that->fClipPlaneUnion;
1136
Brian Salomon05441c42017-05-15 16:45:49 -04001137 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001138 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001139 fVertCount += that->fVertCount;
1140 fIndexCount += that->fIndexCount;
1141 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001142 return true;
1143 }
1144
Brian Salomon05441c42017-05-15 16:45:49 -04001145 struct Circle {
Brian Salomon289e3d82016-12-14 15:52:56 -05001146 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001147 SkScalar fInnerRadius;
1148 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001149 SkScalar fClipPlane[3];
1150 SkScalar fIsectPlane[3];
1151 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001152 SkRect fDevBounds;
1153 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001154 };
1155
Brian Salomon289e3d82016-12-14 15:52:56 -05001156 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001157 Helper fHelper;
1158 SkSTArray<1, Circle, true> fCircles;
Brian Salomon289e3d82016-12-14 15:52:56 -05001159 int fVertCount;
1160 int fIndexCount;
1161 bool fAllFill;
1162 bool fClipPlane;
1163 bool fClipPlaneIsect;
1164 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001165
Brian Salomon05441c42017-05-15 16:45:49 -04001166 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001167};
1168
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001169///////////////////////////////////////////////////////////////////////////////
1170
Brian Salomon05441c42017-05-15 16:45:49 -04001171class EllipseOp : public GrMeshDrawOp {
1172private:
1173 using Helper = GrSimpleMeshDrawOpHelper;
1174
1175 struct DeviceSpaceParams {
1176 SkPoint fCenter;
1177 SkScalar fXRadius;
1178 SkScalar fYRadius;
1179 SkScalar fInnerXRadius;
1180 SkScalar fInnerYRadius;
1181 };
1182
joshualitt76e7fb62015-02-11 08:52:27 -08001183public:
Brian Salomon25a88092016-12-01 09:36:50 -05001184 DEFINE_OP_CLASS_ID
Brian Salomon05441c42017-05-15 16:45:49 -04001185 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1186 const SkRect& ellipse, const SkStrokeRec& stroke) {
1187 DeviceSpaceParams params;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001188 // do any matrix crunching before we reset the draw state for device coords
Brian Salomon05441c42017-05-15 16:45:49 -04001189 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1190 viewMatrix.mapPoints(&params.fCenter, 1);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001191 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1192 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon05441c42017-05-15 16:45:49 -04001193 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1194 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1195 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1196 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001197
bsalomon4b4a7cc2016-07-08 04:42:54 -07001198 // do (potentially) anisotropic mapping of stroke
1199 SkVector scaledStroke;
1200 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001201 scaledStroke.fX = SkScalarAbs(
1202 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1203 scaledStroke.fY = SkScalarAbs(
1204 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001205
1206 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001207 bool isStrokeOnly =
1208 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001209 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1210
Brian Salomon05441c42017-05-15 16:45:49 -04001211 params.fInnerXRadius = 0;
1212 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001213 if (hasStroke) {
1214 if (SkScalarNearlyZero(scaledStroke.length())) {
1215 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1216 } else {
1217 scaledStroke.scale(SK_ScalarHalf);
1218 }
1219
1220 // we only handle thick strokes for near-circular ellipses
1221 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001222 (0.5f * params.fXRadius > params.fYRadius ||
1223 0.5f * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001224 return nullptr;
1225 }
1226
1227 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001228 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1229 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1230 scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1231 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001232 return nullptr;
1233 }
1234
1235 // this is legit only if scale & translation (which should be the case at the moment)
1236 if (isStrokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04001237 params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1238 params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001239 }
1240
Brian Salomon05441c42017-05-15 16:45:49 -04001241 params.fXRadius += scaledStroke.fX;
1242 params.fYRadius += scaledStroke.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001243 }
Brian Salomon05441c42017-05-15 16:45:49 -04001244 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1245 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001246
Brian Salomon05441c42017-05-15 16:45:49 -04001247 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1248 const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1249 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1250 SkStrokeRec::Style style = stroke.getStyle();
1251 bool isStrokeOnly =
1252 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001253
Brian Salomon05441c42017-05-15 16:45:49 -04001254 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1255 params.fInnerXRadius, params.fInnerYRadius,
1256 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1257 params.fCenter.fY - params.fYRadius,
1258 params.fCenter.fX + params.fXRadius,
1259 params.fCenter.fY + params.fYRadius)});
1260
1261 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001262
bsalomon4b4a7cc2016-07-08 04:42:54 -07001263 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon05441c42017-05-15 16:45:49 -04001264 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001265
Brian Salomon05441c42017-05-15 16:45:49 -04001266 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1267 fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomoncdaa97b2016-03-08 08:30:14 -08001268 }
joshualitt76e7fb62015-02-11 08:52:27 -08001269
Brian Salomon289e3d82016-12-14 15:52:56 -05001270 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001271
Brian Salomon7c3e7182016-12-01 09:35:30 -05001272 SkString dumpInfo() const override {
1273 SkString string;
1274 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04001275 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001276 string.appendf(
1277 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1278 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1279 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1280 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1281 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001282 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001283 string.append(INHERITED::dumpInfo());
1284 return string;
1285 }
1286
Brian Salomon05441c42017-05-15 16:45:49 -04001287 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1288 GrColor* color = &fEllipses.front().fColor;
1289 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1290 color);
1291 }
1292
1293 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1294
bsalomone46f9fe2015-08-18 06:05:14 -07001295private:
joshualitt144c3c82015-11-30 12:30:13 -08001296 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001297 SkMatrix localMatrix;
1298 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001299 return;
1300 }
1301
1302 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001303 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001304
bsalomonb5238a72015-05-05 07:49:49 -07001305 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001306 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001307 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon05441c42017-05-15 16:45:49 -04001308 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1309 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001310 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001311 return;
1312 }
1313
Brian Salomon05441c42017-05-15 16:45:49 -04001314 for (const auto& ellipse : fEllipses) {
1315 GrColor color = ellipse.fColor;
1316 SkScalar xRadius = ellipse.fXRadius;
1317 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001318
1319 // Compute the reciprocals of the radii here to save time in the shader
1320 SkScalar xRadRecip = SkScalarInvert(xRadius);
1321 SkScalar yRadRecip = SkScalarInvert(yRadius);
Brian Salomon05441c42017-05-15 16:45:49 -04001322 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1323 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001324
vjiaoblack977996d2016-06-30 12:20:54 -07001325 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1326 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1327 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1328
joshualitt76e7fb62015-02-11 08:52:27 -08001329 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon05441c42017-05-15 16:45:49 -04001330 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001331 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001332 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001333 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1334 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1335
Brian Salomon05441c42017-05-15 16:45:49 -04001336 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001337 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001338 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001339 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1340 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1341
Brian Salomon05441c42017-05-15 16:45:49 -04001342 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001343 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001344 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001345 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1346 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1347
Brian Salomon05441c42017-05-15 16:45:49 -04001348 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001349 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001350 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001351 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1352 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1353
bsalomonb5238a72015-05-05 07:49:49 -07001354 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001355 }
Brian Salomon05441c42017-05-15 16:45:49 -04001356 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001357 }
1358
Brian Salomon25a88092016-12-01 09:36:50 -05001359 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001360 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001361
Brian Salomon05441c42017-05-15 16:45:49 -04001362 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001363 return false;
1364 }
1365
bsalomoncdaa97b2016-03-08 08:30:14 -08001366 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001367 return false;
1368 }
1369
Brian Salomon05441c42017-05-15 16:45:49 -04001370 if (fHelper.usesLocalCoords() &&
1371 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001372 return false;
1373 }
1374
Brian Salomon05441c42017-05-15 16:45:49 -04001375 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001376 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001377 return true;
1378 }
1379
Brian Salomon05441c42017-05-15 16:45:49 -04001380 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001381 GrColor fColor;
1382 SkScalar fXRadius;
1383 SkScalar fYRadius;
1384 SkScalar fInnerXRadius;
1385 SkScalar fInnerYRadius;
1386 SkRect fDevBounds;
1387 };
joshualitt76e7fb62015-02-11 08:52:27 -08001388
Brian Salomon289e3d82016-12-14 15:52:56 -05001389 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04001390 Helper fHelper;
1391 bool fStroked;
1392 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001393
Brian Salomon05441c42017-05-15 16:45:49 -04001394 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001395};
1396
joshualitt76e7fb62015-02-11 08:52:27 -08001397/////////////////////////////////////////////////////////////////////////////////////////////////
1398
Brian Salomon05441c42017-05-15 16:45:49 -04001399class DIEllipseOp : public GrMeshDrawOp {
1400private:
1401 using Helper = GrSimpleMeshDrawOpHelper;
1402
1403 struct DeviceSpaceParams {
1404 SkPoint fCenter;
1405 SkScalar fXRadius;
1406 SkScalar fYRadius;
1407 SkScalar fInnerXRadius;
1408 SkScalar fInnerYRadius;
1409 DIEllipseStyle fStyle;
1410 };
1411
joshualitt76e7fb62015-02-11 08:52:27 -08001412public:
Brian Salomon25a88092016-12-01 09:36:50 -05001413 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001414
Brian Salomon05441c42017-05-15 16:45:49 -04001415 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1416 const SkRect& ellipse, const SkStrokeRec& stroke) {
1417 DeviceSpaceParams params;
1418 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1419 params.fXRadius = SkScalarHalf(ellipse.width());
1420 params.fYRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001421
bsalomon4b4a7cc2016-07-08 04:42:54 -07001422 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon05441c42017-05-15 16:45:49 -04001423 params.fStyle = (SkStrokeRec::kStroke_Style == style)
1424 ? DIEllipseStyle::kStroke
1425 : (SkStrokeRec::kHairline_Style == style)
1426 ? DIEllipseStyle::kHairline
1427 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001428
Brian Salomon05441c42017-05-15 16:45:49 -04001429 params.fInnerXRadius = 0;
1430 params.fInnerYRadius = 0;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001431 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1432 SkScalar strokeWidth = stroke.getWidth();
1433
1434 if (SkScalarNearlyZero(strokeWidth)) {
1435 strokeWidth = SK_ScalarHalf;
1436 } else {
1437 strokeWidth *= SK_ScalarHalf;
1438 }
1439
1440 // we only handle thick strokes for near-circular ellipses
1441 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon05441c42017-05-15 16:45:49 -04001442 (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1443 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001444 return nullptr;
1445 }
1446
1447 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon05441c42017-05-15 16:45:49 -04001448 if (strokeWidth * (params.fYRadius * params.fYRadius) <
1449 (strokeWidth * strokeWidth) * params.fXRadius) {
1450 return nullptr;
1451 }
1452 if (strokeWidth * (params.fXRadius * params.fXRadius) <
1453 (strokeWidth * strokeWidth) * params.fYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 return nullptr;
1455 }
1456
1457 // set inner radius (if needed)
1458 if (SkStrokeRec::kStroke_Style == style) {
Brian Salomon05441c42017-05-15 16:45:49 -04001459 params.fInnerXRadius = params.fXRadius - strokeWidth;
1460 params.fInnerYRadius = params.fYRadius - strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001461 }
1462
Brian Salomon05441c42017-05-15 16:45:49 -04001463 params.fXRadius += strokeWidth;
1464 params.fYRadius += strokeWidth;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001465 }
Brian Salomon05441c42017-05-15 16:45:49 -04001466 if (DIEllipseStyle::kStroke == params.fStyle &&
1467 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1468 params.fStyle = DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001469 }
Brian Salomon05441c42017-05-15 16:45:49 -04001470 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1471 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001472
Brian Salomon05441c42017-05-15 16:45:49 -04001473 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1474 const SkMatrix& viewMatrix)
1475 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001476 // This expands the outer rect so that after CTM we end up with a half-pixel border
1477 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1478 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1479 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1480 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001481 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1482 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001483
Brian Salomon05441c42017-05-15 16:45:49 -04001484 fEllipses.emplace_back(
1485 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1486 params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1487 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1488 params.fCenter.fY - params.fYRadius - geoDy,
1489 params.fCenter.fX + params.fXRadius + geoDx,
1490 params.fCenter.fY + params.fYRadius + geoDy)});
1491 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1492 IsZeroArea::kNo);
joshualitt76e7fb62015-02-11 08:52:27 -08001493 }
1494
Brian Salomon289e3d82016-12-14 15:52:56 -05001495 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001496
Brian Salomon7c3e7182016-12-01 09:35:30 -05001497 SkString dumpInfo() const override {
1498 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001499 for (const auto& geo : fEllipses) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001500 string.appendf(
1501 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1502 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1503 "GeoDY: %.2f\n",
1504 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1505 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1506 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001507 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001508 string.append(INHERITED::dumpInfo());
1509 return string;
1510 }
1511
Brian Salomon05441c42017-05-15 16:45:49 -04001512 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1513 GrColor* color = &fEllipses.front().fColor;
1514 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1515 color);
1516 }
1517
1518 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1519
bsalomone46f9fe2015-08-18 06:05:14 -07001520private:
joshualitt144c3c82015-11-30 12:30:13 -08001521 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001522 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001523 sk_sp<GrGeometryProcessor> gp(
1524 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001525
joshualitt76e7fb62015-02-11 08:52:27 -08001526 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001527 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001528 QuadHelper helper;
1529 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon05441c42017-05-15 16:45:49 -04001530 helper.init(target, vertexStride, fEllipses.count()));
bsalomonb5238a72015-05-05 07:49:49 -07001531 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001532 return;
1533 }
1534
Brian Salomon05441c42017-05-15 16:45:49 -04001535 for (const auto& ellipse : fEllipses) {
1536 GrColor color = ellipse.fColor;
1537 SkScalar xRadius = ellipse.fXRadius;
1538 SkScalar yRadius = ellipse.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001539
Brian Salomon05441c42017-05-15 16:45:49 -04001540 const SkRect& bounds = ellipse.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001541
1542 // This adjusts the "radius" to include the half-pixel border
Brian Salomon05441c42017-05-15 16:45:49 -04001543 SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1544 SkScalar offsetDy = ellipse.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001545
Brian Salomon05441c42017-05-15 16:45:49 -04001546 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1547 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001548
1549 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001550 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001551 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1552 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1553
Brian Salomon289e3d82016-12-14 15:52:56 -05001554 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001555 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001556 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1557 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1558
1559 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001560 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001561 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1562 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1563
1564 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001565 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001566 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1567 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1568
bsalomonb5238a72015-05-05 07:49:49 -07001569 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001570 }
Brian Salomon05441c42017-05-15 16:45:49 -04001571 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08001572 }
halcanary9d524f22016-03-29 09:03:52 -07001573
Brian Salomon25a88092016-12-01 09:36:50 -05001574 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001575 DIEllipseOp* that = t->cast<DIEllipseOp>();
Brian Salomon05441c42017-05-15 16:45:49 -04001576 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07001577 return false;
1578 }
1579
bsalomoncdaa97b2016-03-08 08:30:14 -08001580 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001581 return false;
1582 }
1583
joshualittd96a67b2015-05-05 14:09:05 -07001584 // TODO rewrite to allow positioning on CPU
1585 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001586 return false;
1587 }
1588
Brian Salomon05441c42017-05-15 16:45:49 -04001589 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001590 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001591 return true;
1592 }
1593
Brian Salomon05441c42017-05-15 16:45:49 -04001594 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1595 DIEllipseStyle style() const { return fEllipses[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001596
Brian Salomon05441c42017-05-15 16:45:49 -04001597 struct Ellipse {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001598 SkMatrix fViewMatrix;
1599 GrColor fColor;
1600 SkScalar fXRadius;
1601 SkScalar fYRadius;
1602 SkScalar fInnerXRadius;
1603 SkScalar fInnerYRadius;
1604 SkScalar fGeoDx;
1605 SkScalar fGeoDy;
1606 DIEllipseStyle fStyle;
1607 SkRect fBounds;
1608 };
1609
Brian Salomon05441c42017-05-15 16:45:49 -04001610 Helper fHelper;
1611 SkSTArray<1, Ellipse, true> fEllipses;
reed1b55a962015-09-17 20:16:13 -07001612
Brian Salomon05441c42017-05-15 16:45:49 -04001613 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001614};
1615
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001616///////////////////////////////////////////////////////////////////////////////
1617
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001618// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001619//
1620// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1621// ____________
1622// |_|________|_|
1623// | | | |
1624// | | | |
1625// | | | |
1626// |_|________|_|
1627// |_|________|_|
1628//
1629// For strokes, we don't draw the center quad.
1630//
1631// For circular roundrects, in the case where the stroke width is greater than twice
1632// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001633// in the center. The shared vertices are duplicated so we can set a different outer radius
1634// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001635// ____________
1636// |_|________|_|
1637// | |\ ____ /| |
1638// | | | | | |
1639// | | |____| | |
1640// |_|/______\|_|
1641// |_|________|_|
1642//
1643// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001644//
1645// For filled rrects that need to provide a distance vector we resuse the overstroke
1646// geometry but make the inner rect degenerate (either a point or a horizontal or
1647// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001648
jvanverth84839f62016-08-29 10:16:40 -07001649static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001650 // clang-format off
1651 // overstroke quads
1652 // we place this at the beginning so that we can skip these indices when rendering normally
1653 16, 17, 19, 16, 19, 18,
1654 19, 17, 23, 19, 23, 21,
1655 21, 23, 22, 21, 22, 20,
1656 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001657
Brian Salomon289e3d82016-12-14 15:52:56 -05001658 // corners
1659 0, 1, 5, 0, 5, 4,
1660 2, 3, 7, 2, 7, 6,
1661 8, 9, 13, 8, 13, 12,
1662 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001663
Brian Salomon289e3d82016-12-14 15:52:56 -05001664 // edges
1665 1, 2, 6, 1, 6, 5,
1666 4, 5, 9, 4, 9, 8,
1667 6, 7, 11, 6, 11, 10,
1668 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001669
Brian Salomon289e3d82016-12-14 15:52:56 -05001670 // center
1671 // we place this at the end so that we can ignore these indices when not rendering as filled
1672 5, 6, 10, 5, 10, 9,
1673 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001674};
Brian Salomon289e3d82016-12-14 15:52:56 -05001675
jvanverth84839f62016-08-29 10:16:40 -07001676// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001677static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001678
jvanverth84839f62016-08-29 10:16:40 -07001679// overstroke count is arraysize minus the center indices
1680static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1681// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001682static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001683// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001684static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1685static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001686static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001687
jvanverthc3d0e422016-08-25 08:12:35 -07001688enum RRectType {
1689 kFill_RRectType,
1690 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001691 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001692 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001693};
1694
jvanverth84839f62016-08-29 10:16:40 -07001695static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001696 switch (type) {
1697 case kFill_RRectType:
1698 case kStroke_RRectType:
1699 return kVertsPerStandardRRect;
1700 case kOverstroke_RRectType:
1701 case kFillWithDist_RRectType:
1702 return kVertsPerOverstrokeRRect;
1703 }
1704 SkFAIL("Invalid type");
1705 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001706}
1707
1708static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001709 switch (type) {
1710 case kFill_RRectType:
1711 return kIndicesPerFillRRect;
1712 case kStroke_RRectType:
1713 return kIndicesPerStrokeRRect;
1714 case kOverstroke_RRectType:
1715 case kFillWithDist_RRectType:
1716 return kIndicesPerOverstrokeRRect;
1717 }
1718 SkFAIL("Invalid type");
1719 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001720}
1721
1722static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001723 switch (type) {
1724 case kFill_RRectType:
1725 case kStroke_RRectType:
1726 return gStandardRRectIndices;
1727 case kOverstroke_RRectType:
1728 case kFillWithDist_RRectType:
1729 return gOverstrokeRRectIndices;
1730 }
1731 SkFAIL("Invalid type");
1732 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001733}
1734
joshualitt76e7fb62015-02-11 08:52:27 -08001735///////////////////////////////////////////////////////////////////////////////////////////////////
1736
Robert Phillips79839d42016-10-06 15:03:34 -04001737// For distance computations in the interior of filled rrects we:
1738//
1739// add a interior degenerate (point or line) rect
1740// each vertex of that rect gets -outerRad as its radius
1741// this makes the computation of the distance to the outer edge be negative
1742// negative values are caught and then handled differently in the GP's onEmitCode
1743// each vertex is also given the normalized x & y distance from the interior rect's edge
1744// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1745
Brian Salomon05441c42017-05-15 16:45:49 -04001746class CircularRRectOp : public GrMeshDrawOp {
1747private:
1748 using Helper = GrSimpleMeshDrawOpHelper;
1749
joshualitt76e7fb62015-02-11 08:52:27 -08001750public:
Brian Salomon25a88092016-12-01 09:36:50 -05001751 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001752
bsalomon4b4a7cc2016-07-08 04:42:54 -07001753 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1754 // whether the rrect is only stroked or stroked and filled.
Brian Salomon05441c42017-05-15 16:45:49 -04001755 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, bool needsDistance,
1756 const SkMatrix& viewMatrix, const SkRect& devRect,
1757 float devRadius, float devStrokeWidth, bool strokeOnly) {
1758 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), needsDistance, viewMatrix,
1759 devRect, devRadius, devStrokeWidth,
1760 strokeOnly);
1761 }
1762 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, bool needsDistance,
1763 const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
1764 float devStrokeWidth, bool strokeOnly)
1765 : INHERITED(ClassID())
1766 , fViewMatrixIfUsingLocalCoords(viewMatrix)
1767 , fHelper(helperArgs, GrAAType::kCoverage) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001768 SkRect bounds = devRect;
1769 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1770 SkScalar innerRadius = 0.0f;
1771 SkScalar outerRadius = devRadius;
1772 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001773 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001774 if (devStrokeWidth > 0) {
1775 if (SkScalarNearlyZero(devStrokeWidth)) {
1776 halfWidth = SK_ScalarHalf;
1777 } else {
1778 halfWidth = SkScalarHalf(devStrokeWidth);
1779 }
joshualitt76e7fb62015-02-11 08:52:27 -08001780
bsalomon4b4a7cc2016-07-08 04:42:54 -07001781 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001782 // Outset stroke by 1/4 pixel
1783 devStrokeWidth += 0.25f;
1784 // If stroke is greater than width or height, this is still a fill
1785 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001786 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001787 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001788 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001789 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001790 }
1791 outerRadius += halfWidth;
1792 bounds.outset(halfWidth, halfWidth);
1793 }
Robert Phillips79839d42016-10-06 15:03:34 -04001794 if (kFill_RRectType == type && needsDistance) {
1795 type = kFillWithDist_RRectType;
1796 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001797
bsalomon4b4a7cc2016-07-08 04:42:54 -07001798 // The radii are outset for two reasons. First, it allows the shader to simply perform
1799 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1800 // Second, the outer radius is used to compute the verts of the bounding box that is
1801 // rendered and the outset ensures the box will cover all partially covered by the rrect
1802 // corners.
1803 outerRadius += SK_ScalarHalf;
1804 innerRadius -= SK_ScalarHalf;
1805
bsalomon88cf17d2016-07-08 06:40:56 -07001806 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1807
1808 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001809 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1810
Brian Salomon05441c42017-05-15 16:45:49 -04001811 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001812 fVertCount = rrect_type_to_vert_count(type);
1813 fIndexCount = rrect_type_to_index_count(type);
1814 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001815 }
1816
Brian Salomon289e3d82016-12-14 15:52:56 -05001817 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001818
jvanverthc3d0e422016-08-25 08:12:35 -07001819 SkString dumpInfo() const override {
1820 SkString string;
Brian Salomon05441c42017-05-15 16:45:49 -04001821 for (int i = 0; i < fRRects.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001822 string.appendf(
1823 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1824 "InnerRad: %.2f, OuterRad: %.2f\n",
Brian Salomon05441c42017-05-15 16:45:49 -04001825 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1826 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1827 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001828 }
1829 string.append(INHERITED::dumpInfo());
1830 return string;
1831 }
1832
Brian Salomon05441c42017-05-15 16:45:49 -04001833 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
1834 GrColor* color = &fRRects.front().fColor;
1835 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1836 color);
1837 }
1838
1839 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1840
Brian Salomon92aee3d2016-12-21 09:20:25 -05001841private:
Robert Phillips79839d42016-10-06 15:03:34 -04001842 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001843 SkPoint fPos;
1844 GrColor fColor;
1845 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001846 SkScalar fOuterRadius;
1847 SkScalar fInnerRadius;
1848 // No half plane, we don't use it here.
1849 };
1850
Brian Salomon289e3d82016-12-14 15:52:56 -05001851 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1852 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1853 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001854 SkASSERT(smInset < bigInset);
1855
1856 // TL
1857 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1858 (*verts)->fColor = color;
1859 (*verts)->fOffset = SkPoint::Make(xOffset, 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 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001865 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001866 (*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
Brian Salomon289e3d82016-12-14 15:52:56 -05001872 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001873 (*verts)->fColor = color;
1874 (*verts)->fOffset = SkPoint::Make(0, 0);
1875 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001876 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001877 (*verts)++;
1878
Brian Salomon289e3d82016-12-14 15:52:56 -05001879 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001880 (*verts)->fColor = color;
1881 (*verts)->fOffset = SkPoint::Make(0, 0);
1882 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001883 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001884 (*verts)++;
1885
1886 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1887 (*verts)->fColor = color;
1888 (*verts)->fOffset = SkPoint::Make(0, 0);
1889 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001890 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001891 (*verts)++;
1892
1893 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1894 (*verts)->fColor = color;
1895 (*verts)->fOffset = SkPoint::Make(0, 0);
1896 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001897 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001898 (*verts)++;
1899
1900 // BL
1901 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1902 (*verts)->fColor = color;
1903 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1904 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001905 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001906 (*verts)++;
1907
1908 // BR
1909 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1910 (*verts)->fColor = color;
1911 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1912 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001913 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001914 (*verts)++;
1915 }
1916
joshualitt144c3c82015-11-30 12:30:13 -08001917 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001918 // Invert the view matrix as a local matrix (if any other processors require coords).
1919 SkMatrix localMatrix;
1920 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001921 return;
1922 }
1923
1924 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001925 sk_sp<GrGeometryProcessor> gp(
1926 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001927
joshualitt76e7fb62015-02-11 08:52:27 -08001928 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001929 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001930
jvanverth84839f62016-08-29 10:16:40 -07001931 const GrBuffer* vertexBuffer;
1932 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001933
Brian Salomon289e3d82016-12-14 15:52:56 -05001934 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1935 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001936 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001937 SkDebugf("Could not allocate vertices\n");
1938 return;
1939 }
1940
jvanverth84839f62016-08-29 10:16:40 -07001941 const GrBuffer* indexBuffer = nullptr;
1942 int firstIndex = 0;
1943 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1944 if (!indices) {
1945 SkDebugf("Could not allocate indices\n");
1946 return;
1947 }
1948
1949 int currStartVertex = 0;
Brian Salomon05441c42017-05-15 16:45:49 -04001950 for (const auto& rrect : fRRects) {
1951 GrColor color = rrect.fColor;
1952 SkScalar outerRadius = rrect.fOuterRadius;
1953 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001954
Brian Salomon289e3d82016-12-14 15:52:56 -05001955 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1956 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001957
Brian Salomon289e3d82016-12-14 15:52:56 -05001958 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001959 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001960 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomon289e3d82016-12-14 15:52:56 -05001961 SkScalar innerRadius =
Brian Salomon05441c42017-05-15 16:45:49 -04001962 rrect.fType != kFill_RRectType && rrect.fType != kFillWithDist_RRectType
1963 ? rrect.fInnerRadius / rrect.fOuterRadius
1964 : -1.0f / rrect.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001965 for (int i = 0; i < 4; ++i) {
1966 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001967 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001968 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1969 verts->fOuterRadius = outerRadius;
1970 verts->fInnerRadius = innerRadius;
1971 verts++;
1972
1973 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001974 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001975 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1976 verts->fOuterRadius = outerRadius;
1977 verts->fInnerRadius = innerRadius;
1978 verts++;
1979
1980 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001981 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001982 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1983 verts->fOuterRadius = outerRadius;
1984 verts->fInnerRadius = innerRadius;
1985 verts++;
1986
1987 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001988 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001989 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1990 verts->fOuterRadius = outerRadius;
1991 verts->fInnerRadius = innerRadius;
1992 verts++;
1993 }
jvanverthc3d0e422016-08-25 08:12:35 -07001994 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001995 // Effectively this is an additional stroked rrect, with its
1996 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1997 // This will give us correct AA in the center and the correct
1998 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001999 //
jvanvertha4f1af82016-08-29 07:17:47 -07002000 // Also, the outer offset is a constant vector pointing to the right, which
2001 // guarantees that the distance value along the outer rectangle is constant.
Brian Salomon05441c42017-05-15 16:45:49 -04002002 if (kOverstroke_RRectType == rrect.fType) {
2003 SkASSERT(rrect.fInnerRadius <= 0.0f);
Robert Phillips79839d42016-10-06 15:03:34 -04002004
Brian Salomon05441c42017-05-15 16:45:49 -04002005 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002006 // this is the normalized distance from the outer rectangle of this
2007 // geometry to the outer edge
Brian Salomon05441c42017-05-15 16:45:49 -04002008 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07002009
Brian Salomon289e3d82016-12-14 15:52:56 -05002010 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
Brian Salomon05441c42017-05-15 16:45:49 -04002011 overstrokeOuterRadius, 0.0f, rrect.fColor);
Robert Phillips79839d42016-10-06 15:03:34 -04002012 }
jvanverth6a397612016-08-26 08:15:33 -07002013
Brian Salomon05441c42017-05-15 16:45:49 -04002014 if (kFillWithDist_RRectType == rrect.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04002015 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07002016
Robert Phillips79839d42016-10-06 15:03:34 -04002017 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07002018
Brian Salomon289e3d82016-12-14 15:52:56 -05002019 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
Brian Salomon05441c42017-05-15 16:45:49 -04002020 -1.0f, rrect.fColor);
jvanverthc3d0e422016-08-25 08:12:35 -07002021 }
jvanverth84839f62016-08-29 10:16:40 -07002022
Brian Salomon05441c42017-05-15 16:45:49 -04002023 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
2024 const int primIndexCount = rrect_type_to_index_count(rrect.fType);
jvanverth84839f62016-08-29 10:16:40 -07002025 for (int i = 0; i < primIndexCount; ++i) {
2026 *indices++ = primIndices[i] + currStartVertex;
2027 }
2028
Brian Salomon05441c42017-05-15 16:45:49 -04002029 currStartVertex += rrect_type_to_vert_count(rrect.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08002030 }
2031
Chris Daltonbca46e22017-05-15 11:03:26 -06002032 GrMesh mesh(kTriangles_GrPrimitiveType);
2033 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex);
2034 mesh.setVertices(vertexBuffer, fVertCount, firstVertex);
Brian Salomon05441c42017-05-15 16:45:49 -04002035 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002036 }
2037
Brian Salomon25a88092016-12-01 09:36:50 -05002038 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002039 CircularRRectOp* that = t->cast<CircularRRectOp>();
Jim Van Verth8cefe402017-02-09 11:36:37 -05002040
2041 // can only represent 65535 unique vertices with 16-bit indices
Jim Van Verthe549a052017-02-21 17:55:13 -05002042 if (fVertCount + that->fVertCount > 65536) {
Jim Van Verth8cefe402017-02-09 11:36:37 -05002043 return false;
2044 }
2045
Brian Salomon05441c42017-05-15 16:45:49 -04002046 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002047 return false;
2048 }
2049
Brian Salomon05441c42017-05-15 16:45:49 -04002050 if (fHelper.usesLocalCoords() &&
2051 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002052 return false;
2053 }
2054
Brian Salomon05441c42017-05-15 16:45:49 -04002055 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002056 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002057 fVertCount += that->fVertCount;
2058 fIndexCount += that->fIndexCount;
2059 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002060 return true;
2061 }
2062
Brian Salomon05441c42017-05-15 16:45:49 -04002063 struct RRect {
Brian Salomon289e3d82016-12-14 15:52:56 -05002064 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002065 SkScalar fInnerRadius;
2066 SkScalar fOuterRadius;
2067 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002068 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002069 };
2070
Brian Salomon289e3d82016-12-14 15:52:56 -05002071 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002072 Helper fHelper;
Brian Salomon289e3d82016-12-14 15:52:56 -05002073 int fVertCount;
2074 int fIndexCount;
2075 bool fAllFill;
Brian Salomon05441c42017-05-15 16:45:49 -04002076 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002077
Brian Salomon05441c42017-05-15 16:45:49 -04002078 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002079};
2080
jvanverth84839f62016-08-29 10:16:40 -07002081static const int kNumRRectsInIndexBuffer = 256;
2082
2083GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2084GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2085static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2086 GrResourceProvider* resourceProvider) {
2087 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2088 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2089 switch (type) {
2090 case kFill_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002091 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002092 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2093 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002094 case kStroke_RRectType:
Chris Daltonff926502017-05-03 14:36:54 -04002095 return resourceProvider->findOrCreatePatternedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002096 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2097 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002098 default:
2099 SkASSERT(false);
2100 return nullptr;
2101 };
2102}
2103
Brian Salomon05441c42017-05-15 16:45:49 -04002104class EllipticalRRectOp : public GrMeshDrawOp {
2105private:
2106 using Helper = GrSimpleMeshDrawOpHelper;
2107
joshualitt76e7fb62015-02-11 08:52:27 -08002108public:
Brian Salomon25a88092016-12-01 09:36:50 -05002109 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002110
bsalomon4b4a7cc2016-07-08 04:42:54 -07002111 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2112 // whether the rrect is only stroked or stroked and filled.
Brian Salomon05441c42017-05-15 16:45:49 -04002113 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2114 const SkRect& devRect, float devXRadius, float devYRadius,
2115 SkVector devStrokeWidths, bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002116 SkASSERT(devXRadius > 0.5);
2117 SkASSERT(devYRadius > 0.5);
2118 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2119 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002120 if (devStrokeWidths.fX > 0) {
2121 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2122 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2123 } else {
2124 devStrokeWidths.scale(SK_ScalarHalf);
2125 }
joshualitt76e7fb62015-02-11 08:52:27 -08002126
bsalomon4b4a7cc2016-07-08 04:42:54 -07002127 // we only handle thick strokes for near-circular ellipses
2128 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002129 (SK_ScalarHalf * devXRadius > devYRadius ||
2130 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002131 return nullptr;
2132 }
2133
2134 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002135 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2136 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002137 return nullptr;
2138 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002139 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2140 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002141 return nullptr;
2142 }
Brian Salomon05441c42017-05-15 16:45:49 -04002143 }
2144 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2145 devXRadius, devYRadius, devStrokeWidths,
2146 strokeOnly);
2147 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07002148
Brian Salomon05441c42017-05-15 16:45:49 -04002149 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2150 const SkRect& devRect, float devXRadius, float devYRadius,
2151 SkVector devStrokeHalfWidths, bool strokeOnly)
2152 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2153 SkScalar innerXRadius = 0.0f;
2154 SkScalar innerYRadius = 0.0f;
2155 SkRect bounds = devRect;
2156 bool stroked = false;
2157 if (devStrokeHalfWidths.fX > 0) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002158 // this is legit only if scale & translation (which should be the case at the moment)
2159 if (strokeOnly) {
Brian Salomon05441c42017-05-15 16:45:49 -04002160 innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2161 innerYRadius = devYRadius - devStrokeHalfWidths.fY;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002162 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2163 }
2164
Brian Salomon05441c42017-05-15 16:45:49 -04002165 devXRadius += devStrokeHalfWidths.fX;
2166 devYRadius += devStrokeHalfWidths.fY;
2167 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002168 }
2169
Brian Salomon05441c42017-05-15 16:45:49 -04002170 fStroked = stroked;
2171 fViewMatrixIfUsingLocalCoords = viewMatrix;
2172 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002173 // Expand the rect for aa in order to generate the correct vertices.
2174 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon05441c42017-05-15 16:45:49 -04002175 fRRects.emplace_back(
2176 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
joshualitt76e7fb62015-02-11 08:52:27 -08002177 }
2178
Brian Salomon289e3d82016-12-14 15:52:56 -05002179 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002180
Brian Salomon7c3e7182016-12-01 09:35:30 -05002181 SkString dumpInfo() const override {
2182 SkString string;
2183 string.appendf("Stroked: %d\n", fStroked);
Brian Salomon05441c42017-05-15 16:45:49 -04002184 for (const auto& geo : fRRects) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002185 string.appendf(
2186 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2187 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2188 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2189 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2190 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002191 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05002192 string.append(INHERITED::dumpInfo());
2193 return string;
2194 }
2195
Brian Salomon05441c42017-05-15 16:45:49 -04002196 bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
2197 GrColor* color = &fRRects.front().fColor;
2198 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2199 color);
2200 }
2201
2202 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2203
bsalomone46f9fe2015-08-18 06:05:14 -07002204private:
joshualitt144c3c82015-11-30 12:30:13 -08002205 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002206 SkMatrix localMatrix;
2207 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002208 return;
2209 }
2210
2211 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002212 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002213
joshualitt76e7fb62015-02-11 08:52:27 -08002214 size_t vertexStride = gp->getVertexStride();
2215 SkASSERT(vertexStride == sizeof(EllipseVertex));
2216
bsalomonb5238a72015-05-05 07:49:49 -07002217 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002218 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002219 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2220 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002221
Chris Daltonbca46e22017-05-15 11:03:26 -06002222 PatternHelper helper(kTriangles_GrPrimitiveType);
bsalomonb5238a72015-05-05 07:49:49 -07002223 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Chris Daltonbca46e22017-05-15 11:03:26 -06002224 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
Brian Salomon05441c42017-05-15 16:45:49 -04002225 indicesPerInstance, fRRects.count()));
bsalomonb5238a72015-05-05 07:49:49 -07002226 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002227 SkDebugf("Could not allocate vertices\n");
2228 return;
2229 }
2230
Brian Salomon05441c42017-05-15 16:45:49 -04002231 for (const auto& rrect : fRRects) {
2232 GrColor color = rrect.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08002233 // Compute the reciprocals of the radii here to save time in the shader
Brian Salomon05441c42017-05-15 16:45:49 -04002234 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2235 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2236 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2237 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08002238
2239 // Extend the radii out half a pixel to antialias.
Brian Salomon05441c42017-05-15 16:45:49 -04002240 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2241 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
joshualitt76e7fb62015-02-11 08:52:27 -08002242
Brian Salomon05441c42017-05-15 16:45:49 -04002243 const SkRect& bounds = rrect.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002244
Brian Salomon289e3d82016-12-14 15:52:56 -05002245 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2246 bounds.fBottom - yOuterRadius, bounds.fBottom};
2247 SkScalar yOuterOffsets[4] = {yOuterRadius,
2248 SK_ScalarNearlyZero, // we're using inversesqrt() in
2249 // shader, so can't be exactly 0
2250 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002251
2252 for (int i = 0; i < 4; ++i) {
2253 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002254 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002255 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2256 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2257 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2258 verts++;
2259
2260 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002261 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002262 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2263 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2264 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2265 verts++;
2266
2267 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002268 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002269 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2270 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2271 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2272 verts++;
2273
2274 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002275 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002276 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2277 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2278 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2279 verts++;
2280 }
2281 }
Brian Salomon05441c42017-05-15 16:45:49 -04002282 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
joshualitt76e7fb62015-02-11 08:52:27 -08002283 }
2284
Brian Salomon25a88092016-12-01 09:36:50 -05002285 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002286 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002287
Brian Salomon05441c42017-05-15 16:45:49 -04002288 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
joshualitt8cab9a72015-07-16 09:13:50 -07002289 return false;
2290 }
2291
bsalomoncdaa97b2016-03-08 08:30:14 -08002292 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002293 return false;
2294 }
2295
Brian Salomon05441c42017-05-15 16:45:49 -04002296 if (fHelper.usesLocalCoords() &&
2297 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002298 return false;
2299 }
2300
Brian Salomon05441c42017-05-15 16:45:49 -04002301 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002302 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002303 return true;
2304 }
2305
Brian Salomon05441c42017-05-15 16:45:49 -04002306 struct RRect {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002307 GrColor fColor;
2308 SkScalar fXRadius;
2309 SkScalar fYRadius;
2310 SkScalar fInnerXRadius;
2311 SkScalar fInnerYRadius;
2312 SkRect fDevBounds;
2313 };
2314
Brian Salomon289e3d82016-12-14 15:52:56 -05002315 SkMatrix fViewMatrixIfUsingLocalCoords;
Brian Salomon05441c42017-05-15 16:45:49 -04002316 Helper fHelper;
2317 bool fStroked;
2318 SkSTArray<1, RRect, true> fRRects;
reed1b55a962015-09-17 20:16:13 -07002319
Brian Salomon05441c42017-05-15 16:45:49 -04002320 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002321};
2322
Brian Salomon05441c42017-05-15 16:45:49 -04002323static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
2324 bool needsDistance,
2325 const SkMatrix& viewMatrix,
2326 const SkRRect& rrect,
2327 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002328 SkASSERT(viewMatrix.rectStaysRect());
2329 SkASSERT(rrect.isSimple());
2330 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002331
Brian Salomon53e4c3c2016-12-21 11:38:53 -05002332 // RRect ops only handle simple, but not too simple, rrects.
2333 // Do any matrix crunching before we reset the draw state for device coords.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002334 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002335 SkRect bounds;
2336 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002337
2338 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002339 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2340 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2341 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2342 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002343
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002344 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002345
bsalomon4b4a7cc2016-07-08 04:42:54 -07002346 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2347 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002348 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002349
Brian Salomon289e3d82016-12-14 15:52:56 -05002350 bool isStrokeOnly =
2351 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002352 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2353
jvanverthc3d0e422016-08-25 08:12:35 -07002354 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002355 if (hasStroke) {
2356 if (SkStrokeRec::kHairline_Style == style) {
2357 scaledStroke.set(1, 1);
2358 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002359 scaledStroke.fX = SkScalarAbs(
2360 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2361 scaledStroke.fY = SkScalarAbs(
2362 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002363 }
2364
jvanverthc3d0e422016-08-25 08:12:35 -07002365 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2366 // for non-circular rrects, if half of strokewidth is greater than radius,
2367 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002368 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2369 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002370 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002371 }
2372 }
2373
2374 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2375 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2376 // patch will have fractional coverage. This only matters when the interior is actually filled.
2377 // We could consider falling back to rect rendering here, since a tiny radius is
2378 // indistinguishable from a square corner.
2379 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002380 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002381 }
2382
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002383 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002384 if (isCircular) {
Brian Salomon05441c42017-05-15 16:45:49 -04002385 return CircularRRectOp::Make(std::move(paint), needsDistance, viewMatrix, bounds, xRadius,
2386 scaledStroke.fX, isStrokeOnly);
Brian Salomon289e3d82016-12-14 15:52:56 -05002387 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002388 } else {
Brian Salomon05441c42017-05-15 16:45:49 -04002389 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2390 scaledStroke, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002391 }
joshualitt3e708c52015-04-30 13:49:27 -07002392}
2393
Brian Salomon05441c42017-05-15 16:45:49 -04002394std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
2395 bool needsDistance,
2396 const SkMatrix& viewMatrix,
2397 const SkRRect& rrect,
2398 const SkStrokeRec& stroke,
2399 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002400 if (rrect.isOval()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002401 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002402 }
2403
2404 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002405 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002406 }
2407
Brian Salomon05441c42017-05-15 16:45:49 -04002408 return make_rrect_op(std::move(paint), needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002409}
joshualitt3e708c52015-04-30 13:49:27 -07002410
bsalomon4b4a7cc2016-07-08 04:42:54 -07002411///////////////////////////////////////////////////////////////////////////////
2412
Brian Salomon05441c42017-05-15 16:45:49 -04002413std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2414 const SkMatrix& viewMatrix,
2415 const SkRect& oval,
2416 const SkStrokeRec& stroke,
2417 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002418 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002419 SkScalar width = oval.width();
Jim Van Verthd952a992017-04-20 17:25:26 -04002420 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2421 circle_stays_circle(viewMatrix)) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002422 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon05441c42017-05-15 16:45:49 -04002423 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2424 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002425 }
2426
Stan Ilieveb868aa2017-02-21 11:06:16 -05002427 // prefer the device space ellipse op for batchability
bsalomon4b4a7cc2016-07-08 04:42:54 -07002428 if (viewMatrix.rectStaysRect()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002429 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002430 }
2431
Stan Ilieveb868aa2017-02-21 11:06:16 -05002432 // Otherwise, if we have shader derivative support, render as device-independent
2433 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon05441c42017-05-15 16:45:49 -04002434 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
Stan Ilieveb868aa2017-02-21 11:06:16 -05002435 }
2436
bsalomon4b4a7cc2016-07-08 04:42:54 -07002437 return nullptr;
2438}
2439
2440///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002441
Brian Salomon05441c42017-05-15 16:45:49 -04002442std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2443 const SkRect& oval, SkScalar startAngle,
2444 SkScalar sweepAngle, bool useCenter,
2445 const GrStyle& style,
2446 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002447 SkASSERT(!oval.isEmpty());
2448 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002449 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002450 if (SkScalarAbs(sweepAngle) >= 360.f) {
2451 return nullptr;
2452 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002453 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2454 return nullptr;
2455 }
2456 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002457 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2458 useCenter};
Brian Salomon05441c42017-05-15 16:45:49 -04002459 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002460}
2461
2462///////////////////////////////////////////////////////////////////////////////
2463
Hal Canary6f6961e2017-01-31 13:50:44 -05002464#if GR_TEST_UTILS
joshualitt3e708c52015-04-30 13:49:27 -07002465
Brian Salomon05441c42017-05-15 16:45:49 -04002466GR_DRAW_OP_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002467 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002468 SkScalar rotate = random->nextSScalar1() * 360.f;
2469 SkScalar translateX = random->nextSScalar1() * 1000.f;
2470 SkScalar translateY = random->nextSScalar1() * 1000.f;
2471 SkScalar scale = random->nextSScalar1() * 100.f;
2472 SkMatrix viewMatrix;
2473 viewMatrix.setRotate(rotate);
2474 viewMatrix.postTranslate(translateX, translateY);
2475 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002476 SkRect circle = GrTest::TestSquare(random);
2477 SkPoint center = {circle.centerX(), circle.centerY()};
2478 SkScalar radius = circle.width() / 2.f;
2479 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002480 CircleOp::ArcParams arcParamsTmp;
2481 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002482 if (random->nextBool()) {
2483 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002484 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2485 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002486 arcParams = &arcParamsTmp;
2487 }
Brian Salomon05441c42017-05-15 16:45:49 -04002488 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2489 GrStyle(stroke, nullptr), arcParams);
Brian Salomon289e3d82016-12-14 15:52:56 -05002490 if (op) {
Brian Salomon5ec9def2016-12-20 15:34:05 -05002491 return op;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002492 }
2493 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002494}
2495
Brian Salomon05441c42017-05-15 16:45:49 -04002496GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002497 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt6c891102015-05-13 08:51:49 -07002498 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002499 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002500}
2501
Brian Salomon05441c42017-05-15 16:45:49 -04002502GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002503 SkMatrix viewMatrix = GrTest::TestMatrix(random);
joshualitt6c891102015-05-13 08:51:49 -07002504 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon05441c42017-05-15 16:45:49 -04002505 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002506}
2507
Brian Salomon05441c42017-05-15 16:45:49 -04002508GR_DRAW_OP_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002509 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
joshualitt3e708c52015-04-30 13:49:27 -07002510 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002511 bool needsDistance = random->nextBool();
Brian Salomon05441c42017-05-15 16:45:49 -04002512 return make_rrect_op(std::move(paint), needsDistance, viewMatrix, rrect,
2513 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002514}
2515
2516#endif