blob: ca38bf223252eb5d4b65e71147bdeed3e86a31fa [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Brian Salomon289e3d82016-12-14 15:52:56 -05008#include "GrOvalOpFactory.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +00009
joshualitt3e708c52015-04-30 13:49:27 -070010#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080011#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080012#include "GrInvariantOutput.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050013#include "GrOpFlushState.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050016#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070017#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000018#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000019#include "SkStrokeRec.h"
Brian Salomondad29232016-12-01 16:40:24 -050020#include "batches/GrMeshDrawOp.h"
egdaniel2d721d32015-11-11 13:06:05 -080021#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080022#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070023#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080024#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080025#include "glsl/GrGLSLUtil.h"
Brian Salomondad29232016-12-01 16:40:24 -050026#include "glsl/GrGLSLVarying.h"
27#include "glsl/GrGLSLVertexShaderBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000028
Brian Salomon25a88092016-12-01 09:36:50 -050029// TODO(joshualitt) - Break this file up during GrOp post implementation cleanup
joshualitt76e7fb62015-02-11 08:52:27 -080030
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080032
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033struct EllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050034 SkPoint fPos;
35 GrColor fColor;
36 SkPoint fOffset;
37 SkPoint fOuterRadii;
38 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000039};
40
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000041struct DIEllipseVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -050042 SkPoint fPos;
43 GrColor fColor;
44 SkPoint fOuterOffset;
45 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046};
47
Brian Salomon289e3d82016-12-14 15:52:56 -050048static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
commit-bot@chromium.org81312832013-03-22 18:34:09 +000049}
50
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000051///////////////////////////////////////////////////////////////////////////////
52
53/**
bsalomonce1c8862014-12-15 07:11:22 -080054 * The output of this effect is a modulation of the input color and coverage for a circle. It
55 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080056 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080057 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec4f : (p.xy, outerRad, innerRad)
60 * p is the position in the normalized space.
61 * outerRad is the outerRadius in device space.
62 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040063 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070064 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
65 * vec4f : (v.xy, outerDistance, innerDistance)
66 * v is a normalized vector pointing to the outer edge
67 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
68 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070069 * Additional clip planes are supported for rendering circular arcs. The additional planes are
70 * either intersected or unioned together. Up to three planes are supported (an initial plane,
71 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
72 * are useful for any given arc, but having all three in one instance allows batching different
73 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074 */
75
bsalomoncdaa97b2016-03-08 08:30:14 -080076class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070078 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
79 const SkMatrix& localMatrix)
80 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080081 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070082 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
83 kHigh_GrSLPrecision);
84 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
85 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070086 if (clipPlane) {
87 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
88 } else {
89 fInClipPlane = nullptr;
90 }
91 if (isectPlane) {
92 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
93 } else {
94 fInIsectPlane = nullptr;
95 }
96 if (unionPlane) {
97 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
98 } else {
99 fInUnionPlane = nullptr;
100 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800101 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000102 }
103
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400104 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700105
bsalomoncdaa97b2016-03-08 08:30:14 -0800106 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
mtklein36352bf2015-03-25 18:17:31 -0700108 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
Brian Salomon94efbf52016-11-29 13:43:05 -0500110 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700111 GLSLProcessor::GenKey(*this, caps, b);
112 }
113
Brian Salomon94efbf52016-11-29 13:43:05 -0500114 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700115 return new GLSLProcessor();
116 }
117
118private:
egdaniel57d3b032015-11-13 11:57:27 -0800119 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000120 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800121 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122
Brian Salomon289e3d82016-12-14 15:52:56 -0500123 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800124 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800125 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800126 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800127 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700128 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800129
joshualittabb52a12015-01-13 15:02:10 -0800130 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800131 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700132 fragBuilder->codeAppend("vec4 circleEdge;");
133 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
134 if (cgp.fInClipPlane) {
135 fragBuilder->codeAppend("vec3 clipPlane;");
136 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
137 }
138 if (cgp.fInIsectPlane) {
139 SkASSERT(cgp.fInClipPlane);
140 fragBuilder->codeAppend("vec3 isectPlane;");
141 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
142 }
143 if (cgp.fInUnionPlane) {
144 SkASSERT(cgp.fInClipPlane);
145 fragBuilder->codeAppend("vec3 unionPlane;");
146 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
147 }
joshualittabb52a12015-01-13 15:02:10 -0800148
joshualittb8c241a2015-05-19 08:23:30 -0700149 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700150 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800151
joshualittabb52a12015-01-13 15:02:10 -0800152 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700153 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800154
155 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800156 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800157 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800158 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800159 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700160 cgp.fInPosition->fName,
161 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700162 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800163
bsalomon4f3a0ca2016-08-22 13:14:26 -0700164 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
165 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
166 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800167 if (cgp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500168 fragBuilder->codeAppend(
169 "float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700170 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800171 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000172 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000173
dvonbeck68f2f7d2016-08-01 11:37:45 -0700174 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700175 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
Brian Salomon289e3d82016-12-14 15:52:56 -0500176 fragBuilder->codeAppendf(
177 "if (d == 0.0) {" // if on the center of the circle
178 " %s = vec4(1.0, 0.0, distanceToOuterEdge, "
179 " %s);", // no normalize
180 args.fDistanceVectorName,
181 innerEdgeDistance);
182 fragBuilder->codeAppendf(
183 "} else {"
184 " %s = vec4(normalize(circleEdge.xy),"
185 " distanceToOuterEdge, %s);"
186 "}",
187 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700188 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700189 if (cgp.fInClipPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500190 fragBuilder->codeAppend(
191 "float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
192 "clipPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700193 if (cgp.fInIsectPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500194 fragBuilder->codeAppend(
195 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
196 "isectPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700197 }
198 if (cgp.fInUnionPlane) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500199 fragBuilder->codeAppend(
200 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
201 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700202 }
203 fragBuilder->codeAppend("edgeAlpha *= clip;");
204 }
egdaniel4ca2e602015-11-18 08:01:26 -0800205 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000206 }
207
robertphillips46d36f02015-01-18 08:14:14 -0800208 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500209 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700210 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800211 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700212 uint16_t key;
Brian Salomon289e3d82016-12-14 15:52:56 -0500213 key = cgp.fStroke ? 0x01 : 0x0;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700214 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
Brian Salomon289e3d82016-12-14 15:52:56 -0500215 key |= cgp.fInClipPlane ? 0x04 : 0x0;
216 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
217 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700218 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219 }
220
bsalomona624bf32016-09-20 09:12:47 -0700221 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
222 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700223 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700224 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700225 }
226
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 private:
egdaniele659a582015-11-13 09:55:43 -0800228 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229 };
230
Brian Salomon289e3d82016-12-14 15:52:56 -0500231 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800232 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800233 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800234 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700235 const Attribute* fInClipPlane;
236 const Attribute* fInIsectPlane;
237 const Attribute* fInUnionPlane;
Brian Salomon289e3d82016-12-14 15:52:56 -0500238 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239
joshualittb0a8a372014-09-23 09:50:21 -0700240 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241
joshualitt249af152014-09-15 11:41:13 -0700242 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243};
244
bsalomoncdaa97b2016-03-08 08:30:14 -0800245GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246
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}
252
253///////////////////////////////////////////////////////////////////////////////
254
255/**
256 * 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 +0000257 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
258 * in both x and y directions.
259 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000260 * 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 +0000261 */
262
bsalomoncdaa97b2016-03-08 08:30:14 -0800263class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500265 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800266 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700267 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
268 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
269 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
270 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800271 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000272 }
273
bsalomoncdaa97b2016-03-08 08:30:14 -0800274 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000275
mtklein36352bf2015-03-25 18:17:31 -0700276 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800277
Brian Salomon94efbf52016-11-29 13:43:05 -0500278 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700279 GLSLProcessor::GenKey(*this, caps, b);
280 }
281
Brian Salomon94efbf52016-11-29 13:43:05 -0500282 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700283 return new GLSLProcessor();
284 }
285
286private:
egdaniel57d3b032015-11-13 11:57:27 -0800287 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000288 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800289 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290
Brian Salomon289e3d82016-12-14 15:52:56 -0500291 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800292 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800293 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800294 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800295 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000296
joshualittabb52a12015-01-13 15:02:10 -0800297 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800298 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800299
egdaniel8dcdedc2015-11-11 06:27:20 -0800300 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800301 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800302 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700303 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000304
egdaniel8dcdedc2015-11-11 06:27:20 -0800305 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800306 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
Brian Salomon289e3d82016-12-14 15:52:56 -0500307 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800308
cdalton85285412016-02-18 12:37:07 -0800309 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700310 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700311 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800312
joshualittabb52a12015-01-13 15:02:10 -0800313 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700314 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800315
316 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800317 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800318 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800319 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800320 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700321 egp.fInPosition->fName,
322 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700323 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800324
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000325 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800326 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
327 ellipseRadii.fsIn());
328 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
329 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
330 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700331
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000332 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800333 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
334 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800335 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000336
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000337 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800338 if (egp.fStroke) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500339 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
egdaniel4ca2e602015-11-18 08:01:26 -0800340 ellipseRadii.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500341 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
342 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800343 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
344 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000345 }
346
egdaniel4ca2e602015-11-18 08:01:26 -0800347 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000348 }
349
robertphillips46d36f02015-01-18 08:14:14 -0800350 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500351 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700352 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800353 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
354 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700355 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700356 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357 }
358
bsalomona624bf32016-09-20 09:12:47 -0700359 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
360 FPCoordTransformIter&& transformIter) override {
361 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
362 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700363 }
364
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365 private:
egdaniele659a582015-11-13 09:55:43 -0800366 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367 };
368
joshualitt71c92602015-01-14 08:12:47 -0800369 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800370 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800371 const Attribute* fInEllipseOffset;
372 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700373 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000374 bool fStroke;
375
joshualittb0a8a372014-09-23 09:50:21 -0700376 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377
joshualitt249af152014-09-15 11:41:13 -0700378 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000379};
380
bsalomoncdaa97b2016-03-08 08:30:14 -0800381GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000382
bungeman06ca8ec2016-06-09 08:01:03 -0700383sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
384 return sk_sp<GrGeometryProcessor>(
Brian Salomon289e3d82016-12-14 15:52:56 -0500385 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000386}
387
388///////////////////////////////////////////////////////////////////////////////
389
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000390/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000391 * 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 +0000392 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
393 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
394 * using differentials.
395 *
396 * The result is device-independent and can be used with any affine matrix.
397 */
398
bsalomoncdaa97b2016-03-08 08:30:14 -0800399enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000400
bsalomoncdaa97b2016-03-08 08:30:14 -0800401class DIEllipseGeometryProcessor : public GrGeometryProcessor {
402public:
403 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
Brian Salomon289e3d82016-12-14 15:52:56 -0500404 : fViewMatrix(viewMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800405 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700406 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
407 kHigh_GrSLPrecision);
408 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
409 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
410 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800411 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412 }
413
bsalomoncdaa97b2016-03-08 08:30:14 -0800414 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000415
mtklein36352bf2015-03-25 18:17:31 -0700416 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000417
Brian Salomon94efbf52016-11-29 13:43:05 -0500418 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700419 GLSLProcessor::GenKey(*this, caps, b);
420 }
halcanary9d524f22016-03-29 09:03:52 -0700421
Brian Salomon94efbf52016-11-29 13:43:05 -0500422 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700423 return new GLSLProcessor();
424 }
425
426private:
egdaniel57d3b032015-11-13 11:57:27 -0800427 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000428 public:
Brian Salomon289e3d82016-12-14 15:52:56 -0500429 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000430
joshualitt465283c2015-09-11 08:19:35 -0700431 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800432 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800433 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800434 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800435 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000436
joshualittabb52a12015-01-13 15:02:10 -0800437 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800438 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800439
egdaniel8dcdedc2015-11-11 06:27:20 -0800440 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800441 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
Brian Salomon289e3d82016-12-14 15:52:56 -0500442 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700443
egdaniel8dcdedc2015-11-11 06:27:20 -0800444 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800445 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
Brian Salomon289e3d82016-12-14 15:52:56 -0500446 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800447
cdalton85285412016-02-18 12:37:07 -0800448 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700449 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800450
joshualittabb52a12015-01-13 15:02:10 -0800451 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800452 this->setupPosition(vertBuilder,
453 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800454 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700455 diegp.fInPosition->fName,
456 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700457 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800458
459 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800460 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800461 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800462 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800463 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700464 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700465 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800466
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800468 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
469 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
470 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
471 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500472 fragBuilder->codeAppendf(
473 "vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
474 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
475 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000476
egdaniel4ca2e602015-11-18 08:01:26 -0800477 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000478 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800479 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
480 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800481 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000482 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800483 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
484 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000485 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800486 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 }
488
489 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800490 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800491 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
492 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
493 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
494 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
Brian Salomon289e3d82016-12-14 15:52:56 -0500495 fragBuilder->codeAppendf(
496 "grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
497 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
498 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
egdaniel4ca2e602015-11-18 08:01:26 -0800499 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
500 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000501 }
502
egdaniel4ca2e602015-11-18 08:01:26 -0800503 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000504 }
505
robertphillips46d36f02015-01-18 08:14:14 -0800506 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500507 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700508 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800509 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
510 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700511 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700512 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 }
514
bsalomona624bf32016-09-20 09:12:47 -0700515 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
516 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800517 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700518
bsalomon31df31c2016-08-17 09:00:24 -0700519 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
520 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700521 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800522 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700523 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
524 }
bsalomona624bf32016-09-20 09:12:47 -0700525 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000526 }
527
528 private:
joshualitt5559ca22015-05-21 15:50:36 -0700529 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700530 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800531
egdaniele659a582015-11-13 09:55:43 -0800532 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 };
534
joshualitt71c92602015-01-14 08:12:47 -0800535 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800536 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800537 const Attribute* fInEllipseOffsets0;
538 const Attribute* fInEllipseOffsets1;
Brian Salomon289e3d82016-12-14 15:52:56 -0500539 SkMatrix fViewMatrix;
540 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000541
joshualittb0a8a372014-09-23 09:50:21 -0700542 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000543
joshualitt249af152014-09-15 11:41:13 -0700544 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000545};
546
bsalomoncdaa97b2016-03-08 08:30:14 -0800547GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548
bungeman06ca8ec2016-06-09 08:01:03 -0700549sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500550 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
551 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000552}
553
554///////////////////////////////////////////////////////////////////////////////
555
jvanverth6ca48822016-10-07 06:57:32 -0700556// We have two possible cases for geometry for a circle:
557
558// In the case of a normal fill, we draw geometry for the circle as an octagon.
559static const uint16_t gFillCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500560 // enter the octagon
561 // clang-format off
562 0, 1, 8, 1, 2, 8,
563 2, 3, 8, 3, 4, 8,
564 4, 5, 8, 5, 6, 8,
565 6, 7, 8, 7, 0, 8
566 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700567};
568
569// For stroked circles, we use two nested octagons.
570static const uint16_t gStrokeCircleIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -0500571 // enter the octagon
572 // clang-format off
573 0, 1, 9, 0, 9, 8,
574 1, 2, 10, 1, 10, 9,
575 2, 3, 11, 2, 11, 10,
576 3, 4, 12, 3, 12, 11,
577 4, 5, 13, 4, 13, 12,
578 5, 6, 14, 5, 14, 13,
579 6, 7, 15, 6, 15, 14,
580 7, 0, 8, 7, 8, 15,
581 // clang-format on
jvanverth6ca48822016-10-07 06:57:32 -0700582};
583
Brian Salomon289e3d82016-12-14 15:52:56 -0500584
jvanverth6ca48822016-10-07 06:57:32 -0700585static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
586static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
587static const int kVertsPerStrokeCircle = 16;
588static const int kVertsPerFillCircle = 9;
589
590static int circle_type_to_vert_count(bool stroked) {
591 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
592}
593
594static int circle_type_to_index_count(bool stroked) {
595 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
596}
597
598static const uint16_t* circle_type_to_indices(bool stroked) {
599 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
600}
601
602///////////////////////////////////////////////////////////////////////////////
603
Brian Salomon289e3d82016-12-14 15:52:56 -0500604class CircleOp final : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -0800605public:
Brian Salomon25a88092016-12-01 09:36:50 -0500606 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700607
bsalomon4f3a0ca2016-08-22 13:14:26 -0700608 /** Optional extra params to render a partial arc rather than a full circle. */
609 struct ArcParams {
610 SkScalar fStartAngleRadians;
611 SkScalar fSweepAngleRadians;
612 bool fUseCenter;
613 };
Brian Salomon289e3d82016-12-14 15:52:56 -0500614 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
615 SkScalar radius, const GrStyle& style,
616 const ArcParams* arcParams = nullptr) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700617 SkASSERT(circle_stays_circle(viewMatrix));
618 const SkStrokeRec& stroke = style.strokeRec();
619 if (style.hasPathEffect()) {
620 return nullptr;
621 }
622 SkStrokeRec::Style recStyle = stroke.getStyle();
623 if (arcParams) {
624 // Arc support depends on the style.
625 switch (recStyle) {
626 case SkStrokeRec::kStrokeAndFill_Style:
Brian Salomon289e3d82016-12-14 15:52:56 -0500627 // This produces a strange result that this op doesn't implement.
bsalomon4f3a0ca2016-08-22 13:14:26 -0700628 return nullptr;
629 case SkStrokeRec::kFill_Style:
630 // This supports all fills.
631 break;
Brian Salomon289e3d82016-12-14 15:52:56 -0500632 case SkStrokeRec::kStroke_Style: // fall through
bsalomon4f3a0ca2016-08-22 13:14:26 -0700633 case SkStrokeRec::kHairline_Style:
634 // Strokes that don't use the center point are supported with butt cap.
635 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
636 return nullptr;
637 }
638 break;
639 }
640 }
641
bsalomon4b4a7cc2016-07-08 04:42:54 -0700642 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700643 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700644 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800645
Brian Salomon289e3d82016-12-14 15:52:56 -0500646 bool isStrokeOnly =
647 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700648 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700649
jvanverth6ca48822016-10-07 06:57:32 -0700650 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700651 SkScalar outerRadius = radius;
652 SkScalar halfWidth = 0;
653 if (hasStroke) {
654 if (SkScalarNearlyZero(strokeWidth)) {
655 halfWidth = SK_ScalarHalf;
656 } else {
657 halfWidth = SkScalarHalf(strokeWidth);
658 }
659
660 outerRadius += halfWidth;
661 if (isStrokeOnly) {
662 innerRadius = radius - halfWidth;
663 }
664 }
665
666 // The radii are outset for two reasons. First, it allows the shader to simply perform
667 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
668 // Second, the outer radius is used to compute the verts of the bounding box that is
669 // rendered and the outset ensures the box will cover all partially covered by the circle.
670 outerRadius += SK_ScalarHalf;
671 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700672 bool stroked = isStrokeOnly && innerRadius > 0.0f;
Brian Salomon289e3d82016-12-14 15:52:56 -0500673 sk_sp<CircleOp> op(new CircleOp());
674 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700675
bsalomon4f3a0ca2016-08-22 13:14:26 -0700676 // This makes every point fully inside the intersection plane.
677 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
678 // This makes every point fully outside the union plane.
679 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
680 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
681 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700682 if (arcParams) {
683 // The shader operates in a space where the circle is translated to be centered at the
684 // origin. Here we compute points on the unit circle at the starting and ending angles.
685 SkPoint startPoint, stopPoint;
686 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
687 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
688 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
689 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
690 // radial lines. However, in both cases we have to be careful about the half-circle.
691 // case. In that case the two radial lines are equal and so that edge gets clipped
692 // twice. Since the shared edge goes through the center we fall back on the useCenter
693 // case.
Brian Salomon289e3d82016-12-14 15:52:56 -0500694 bool useCenter =
695 (arcParams->fUseCenter || isStrokeOnly) &&
696 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700697 if (useCenter) {
698 SkVector norm0 = {startPoint.fY, -startPoint.fX};
699 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
700 if (arcParams->fSweepAngleRadians > 0) {
701 norm0.negate();
702 } else {
703 norm1.negate();
704 }
Brian Salomon289e3d82016-12-14 15:52:56 -0500705 op->fClipPlane = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700706 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500707 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700708 color,
709 innerRadius,
710 outerRadius,
711 {norm0.fX, norm0.fY, 0.5f},
712 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
713 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700714 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500715 stroked});
716 op->fClipPlaneIsect = false;
717 op->fClipPlaneUnion = true;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700718 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500719 op->fGeoData.emplace_back(Geometry{
bsalomon4f3a0ca2016-08-22 13:14:26 -0700720 color,
721 innerRadius,
722 outerRadius,
723 {norm0.fX, norm0.fY, 0.5f},
724 {norm1.fX, norm1.fY, 0.5f},
725 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700726 devBounds,
Brian Salomon289e3d82016-12-14 15:52:56 -0500727 stroked});
728 op->fClipPlaneIsect = true;
729 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700730 }
731 } else {
732 // We clip to a secant of the original circle.
733 startPoint.scale(radius);
734 stopPoint.scale(radius);
735 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
736 norm.normalize();
737 if (arcParams->fSweepAngleRadians > 0) {
738 norm.negate();
739 }
740 SkScalar d = -norm.dot(startPoint) + 0.5f;
741
Brian Salomon289e3d82016-12-14 15:52:56 -0500742 op->fGeoData.emplace_back(
743 Geometry{color,
744 innerRadius,
745 outerRadius,
746 {norm.fX, norm.fY, d},
747 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
748 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
749 devBounds,
750 stroked});
751 op->fClipPlane = true;
752 op->fClipPlaneIsect = false;
753 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700754 }
755 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -0500756 op->fGeoData.emplace_back(
757 Geometry{color,
758 innerRadius,
759 outerRadius,
760 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
761 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
762 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
763 devBounds,
764 stroked});
765 op->fClipPlane = false;
766 op->fClipPlaneIsect = false;
767 op->fClipPlaneUnion = false;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700768 }
bsalomon88cf17d2016-07-08 06:40:56 -0700769 // Use the original radius and stroke radius for the bounds so that it does not include the
770 // AA bloat.
771 radius += halfWidth;
Brian Salomon289e3d82016-12-14 15:52:56 -0500772 op->setBounds(
773 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
774 HasAABloat::kYes, IsZeroArea::kNo);
775 op->fVertCount = circle_type_to_vert_count(stroked);
776 op->fIndexCount = circle_type_to_index_count(stroked);
777 op->fAllFill = !stroked;
778 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -0800779 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700780
Brian Salomon289e3d82016-12-14 15:52:56 -0500781 const char* name() const override { return "CircleOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800782
robertphillipse004bfc2015-11-16 09:06:59 -0800783 SkString dumpInfo() const override {
784 SkString string;
785 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -0500786 string.appendf(
787 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
788 "InnerRad: %.2f, OuterRad: %.2f\n",
789 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
790 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
791 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
robertphillipse004bfc2015-11-16 09:06:59 -0800792 }
Brian Salomon7c3e7182016-12-01 09:35:30 -0500793 string.append(DumpPipelineInfo(*this->pipeline()));
robertphillipse004bfc2015-11-16 09:06:59 -0800794 string.append(INHERITED::dumpInfo());
795 return string;
796 }
797
halcanary9d524f22016-03-29 09:03:52 -0700798 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800799 GrInitInvariantOutput* coverage,
800 GrBatchToXPOverrides* overrides) const override {
Brian Salomon289e3d82016-12-14 15:52:56 -0500801 // When this is called there is only one circle.
ethannicholasff210322015-11-24 12:10:10 -0800802 color->setKnownFourComponents(fGeoData[0].fColor);
803 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800804 }
805
bsalomone46f9fe2015-08-18 06:05:14 -0700806private:
Brian Salomon289e3d82016-12-14 15:52:56 -0500807 CircleOp() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800808 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800809 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800810 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800811 if (!overrides.readsLocalCoords()) {
812 fViewMatrixIfUsingLocalCoords.reset();
813 }
joshualitt76e7fb62015-02-11 08:52:27 -0800814 }
815
joshualitt144c3c82015-11-30 12:30:13 -0800816 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800817 SkMatrix localMatrix;
818 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800819 return;
820 }
821
822 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -0500823 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
824 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700825
826 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -0500827 SkPoint fPos;
828 GrColor fColor;
829 SkPoint fOffset;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700830 SkScalar fOuterRadius;
831 SkScalar fInnerRadius;
832 // These planes may or may not be present in the vertex buffer.
833 SkScalar fHalfPlanes[3][3];
834 };
joshualitt76e7fb62015-02-11 08:52:27 -0800835
joshualitt76e7fb62015-02-11 08:52:27 -0800836 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800837 size_t vertexStride = gp->getVertexStride();
Brian Salomon289e3d82016-12-14 15:52:56 -0500838 SkASSERT(vertexStride ==
839 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
840 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
841 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700842
843 const GrBuffer* vertexBuffer;
844 int firstVertex;
Brian Salomon289e3d82016-12-14 15:52:56 -0500845 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
846 &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700847 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700848 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800849 return;
850 }
851
jvanverth6ca48822016-10-07 06:57:32 -0700852 const GrBuffer* indexBuffer = nullptr;
853 int firstIndex = 0;
854 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
855 if (!indices) {
856 SkDebugf("Could not allocate indices\n");
857 return;
858 }
859
860 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800861 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800862 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800863
brianosmanbb2ff942016-02-11 14:15:18 -0800864 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700865 SkScalar innerRadius = geom.fInnerRadius;
866 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800867
bsalomonb5238a72015-05-05 07:49:49 -0700868 const SkRect& bounds = geom.fDevBounds;
Brian Salomon289e3d82016-12-14 15:52:56 -0500869 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
870 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
871 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
872 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
873 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
874 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
875 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
876 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800877
878 // The inner radius in the vertex data must be specified in normalized space.
879 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700880
881 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
Brian Salomon289e3d82016-12-14 15:52:56 -0500882 SkScalar halfWidth = 0.5f * bounds.width();
jvanverth6ca48822016-10-07 06:57:32 -0700883 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
Herb Derby60c05f92016-12-13 15:18:55 -0500884
Brian Salomon289e3d82016-12-14 15:52:56 -0500885 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700886 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700887 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700888 v0->fOuterRadius = outerRadius;
889 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800890
Brian Salomon289e3d82016-12-14 15:52:56 -0500891 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700892 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700893 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700894 v1->fOuterRadius = outerRadius;
895 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800896
Brian Salomon289e3d82016-12-14 15:52:56 -0500897 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700899 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700900 v2->fOuterRadius = outerRadius;
901 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800902
Brian Salomon289e3d82016-12-14 15:52:56 -0500903 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700904 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700905 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700906 v3->fOuterRadius = outerRadius;
907 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800908
Brian Salomon289e3d82016-12-14 15:52:56 -0500909 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700910 v4->fColor = color;
911 v4->fOffset = SkPoint::Make(octOffset, 1);
912 v4->fOuterRadius = outerRadius;
913 v4->fInnerRadius = innerRadius;
914
Brian Salomon289e3d82016-12-14 15:52:56 -0500915 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700916 v5->fColor = color;
917 v5->fOffset = SkPoint::Make(-octOffset, 1);
918 v5->fOuterRadius = outerRadius;
919 v5->fInnerRadius = innerRadius;
920
Brian Salomon289e3d82016-12-14 15:52:56 -0500921 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700922 v6->fColor = color;
923 v6->fOffset = SkPoint::Make(-1, octOffset);
924 v6->fOuterRadius = outerRadius;
925 v6->fInnerRadius = innerRadius;
926
Brian Salomon289e3d82016-12-14 15:52:56 -0500927 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
jvanverth6ca48822016-10-07 06:57:32 -0700928 v7->fColor = color;
929 v7->fOffset = SkPoint::Make(-1, -octOffset);
930 v7->fOuterRadius = outerRadius;
931 v7->fInnerRadius = innerRadius;
932
bsalomon4f3a0ca2016-08-22 13:14:26 -0700933 if (fClipPlane) {
934 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
935 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
936 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
937 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700938 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
939 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700942 }
943 int unionIdx = 1;
944 if (fClipPlaneIsect) {
945 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
946 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
947 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
948 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700949 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
950 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700953 unionIdx = 2;
954 }
955 if (fClipPlaneUnion) {
956 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
957 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
958 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
959 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700960 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
961 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700964 }
jvanverth6ca48822016-10-07 06:57:32 -0700965
966 if (geom.fStroked) {
967 // compute the inner ring
968 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
969 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
970 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
971 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
972 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
973 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
974 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
975 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
976
977 // cosine and sine of pi/8
978 SkScalar c = 0.923579533f;
979 SkScalar s = 0.382683432f;
980 SkScalar r = geom.fInnerRadius;
981
Brian Salomon289e3d82016-12-14 15:52:56 -0500982 v0->fPos = center + SkPoint::Make(-s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700983 v0->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500984 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700985 v0->fOuterRadius = outerRadius;
986 v0->fInnerRadius = innerRadius;
987
Brian Salomon289e3d82016-12-14 15:52:56 -0500988 v1->fPos = center + SkPoint::Make(s * r, -c * r);
jvanverth6ca48822016-10-07 06:57:32 -0700989 v1->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500990 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700991 v1->fOuterRadius = outerRadius;
992 v1->fInnerRadius = innerRadius;
993
Brian Salomon289e3d82016-12-14 15:52:56 -0500994 v2->fPos = center + SkPoint::Make(c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -0700995 v2->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -0500996 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -0700997 v2->fOuterRadius = outerRadius;
998 v2->fInnerRadius = innerRadius;
999
Brian Salomon289e3d82016-12-14 15:52:56 -05001000 v3->fPos = center + SkPoint::Make(c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001001 v3->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001002 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001003 v3->fOuterRadius = outerRadius;
1004 v3->fInnerRadius = innerRadius;
1005
Brian Salomon289e3d82016-12-14 15:52:56 -05001006 v4->fPos = center + SkPoint::Make(s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001007 v4->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001008 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001009 v4->fOuterRadius = outerRadius;
1010 v4->fInnerRadius = innerRadius;
1011
Brian Salomon289e3d82016-12-14 15:52:56 -05001012 v5->fPos = center + SkPoint::Make(-s * r, c * r);
jvanverth6ca48822016-10-07 06:57:32 -07001013 v5->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001014 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001015 v5->fOuterRadius = outerRadius;
1016 v5->fInnerRadius = innerRadius;
1017
Brian Salomon289e3d82016-12-14 15:52:56 -05001018 v6->fPos = center + SkPoint::Make(-c * r, s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001019 v6->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001020 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001021 v6->fOuterRadius = outerRadius;
1022 v6->fInnerRadius = innerRadius;
1023
Brian Salomon289e3d82016-12-14 15:52:56 -05001024 v7->fPos = center + SkPoint::Make(-c * r, -s * r);
jvanverth6ca48822016-10-07 06:57:32 -07001025 v7->fColor = color;
Brian Salomon289e3d82016-12-14 15:52:56 -05001026 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
jvanverth6ca48822016-10-07 06:57:32 -07001027 v7->fOuterRadius = outerRadius;
1028 v7->fInnerRadius = innerRadius;
1029
1030 if (fClipPlane) {
1031 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1032 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1033 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1034 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1035 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 }
1040 int unionIdx = 1;
1041 if (fClipPlaneIsect) {
1042 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1043 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1044 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1045 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1046 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 unionIdx = 2;
1051 }
1052 if (fClipPlaneUnion) {
1053 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1054 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1055 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1056 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1057 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 }
1062 } else {
1063 // filled
1064 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1065 v8->fPos = center;
1066 v8->fColor = color;
1067 v8->fOffset = SkPoint::Make(0, 0);
1068 v8->fOuterRadius = outerRadius;
1069 v8->fInnerRadius = innerRadius;
1070 if (fClipPlane) {
1071 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1072 }
1073 int unionIdx = 1;
1074 if (fClipPlaneIsect) {
1075 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1076 unionIdx = 2;
1077 }
1078 if (fClipPlaneUnion) {
1079 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1080 }
1081 }
1082
1083 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1084 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1085 for (int i = 0; i < primIndexCount; ++i) {
1086 *indices++ = primIndices[i] + currStartVertex;
1087 }
1088
1089 currStartVertex += circle_type_to_vert_count(geom.fStroked);
Brian Salomon289e3d82016-12-14 15:52:56 -05001090 vertices += circle_type_to_vert_count(geom.fStroked) * vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001091 }
jvanverth6ca48822016-10-07 06:57:32 -07001092
1093 GrMesh mesh;
1094 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1095 firstIndex, fVertCount, fIndexCount);
1096 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001097 }
1098
Brian Salomon25a88092016-12-01 09:36:50 -05001099 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001100 CircleOp* that = t->cast<CircleOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001101 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1102 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001103 return false;
1104 }
1105
jvanverth6ca48822016-10-07 06:57:32 -07001106 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001107 return false;
1108 }
1109
Brian Salomon289e3d82016-12-14 15:52:56 -05001110 // Because we've set up the ops that don't use the planes with noop values
1111 // we can just accumulate used planes by later ops.
bsalomon4f3a0ca2016-08-22 13:14:26 -07001112 fClipPlane |= that->fClipPlane;
1113 fClipPlaneIsect |= that->fClipPlaneIsect;
1114 fClipPlaneUnion |= that->fClipPlaneUnion;
1115
bsalomoncdaa97b2016-03-08 08:30:14 -08001116 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001117 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001118 fVertCount += that->fVertCount;
1119 fIndexCount += that->fIndexCount;
1120 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001121 return true;
1122 }
1123
bsalomon4b4a7cc2016-07-08 04:42:54 -07001124 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05001125 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001126 SkScalar fInnerRadius;
1127 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 SkScalar fClipPlane[3];
1129 SkScalar fIsectPlane[3];
1130 SkScalar fUnionPlane[3];
Brian Salomon289e3d82016-12-14 15:52:56 -05001131 SkRect fDevBounds;
1132 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001133 };
1134
jvanverth6ca48822016-10-07 06:57:32 -07001135 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05001136 SkMatrix fViewMatrixIfUsingLocalCoords;
1137 int fVertCount;
1138 int fIndexCount;
1139 bool fAllFill;
1140 bool fClipPlane;
1141 bool fClipPlaneIsect;
1142 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001143
Brian Salomondad29232016-12-01 16:40:24 -05001144 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001145};
1146
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001147///////////////////////////////////////////////////////////////////////////////
1148
Brian Salomon289e3d82016-12-14 15:52:56 -05001149class EllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001150public:
Brian Salomon25a88092016-12-01 09:36:50 -05001151 DEFINE_OP_CLASS_ID
Brian Salomon289e3d82016-12-14 15:52:56 -05001152 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
1153 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001154 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001155
bsalomon4b4a7cc2016-07-08 04:42:54 -07001156 // do any matrix crunching before we reset the draw state for device coords
1157 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1158 viewMatrix.mapPoints(&center, 1);
1159 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1160 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
Brian Salomon289e3d82016-12-14 15:52:56 -05001161 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1162 viewMatrix[SkMatrix::kMSkewY] * ellipseYRadius);
1163 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * ellipseXRadius +
1164 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001165
bsalomon4b4a7cc2016-07-08 04:42:54 -07001166 // do (potentially) anisotropic mapping of stroke
1167 SkVector scaledStroke;
1168 SkScalar strokeWidth = stroke.getWidth();
Brian Salomon289e3d82016-12-14 15:52:56 -05001169 scaledStroke.fX = SkScalarAbs(
1170 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1171 scaledStroke.fY = SkScalarAbs(
1172 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001173
1174 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001175 bool isStrokeOnly =
1176 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001177 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1178
1179 SkScalar innerXRadius = 0;
1180 SkScalar innerYRadius = 0;
1181 if (hasStroke) {
1182 if (SkScalarNearlyZero(scaledStroke.length())) {
1183 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1184 } else {
1185 scaledStroke.scale(SK_ScalarHalf);
1186 }
1187
1188 // we only handle thick strokes for near-circular ellipses
1189 if (scaledStroke.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001190 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001191 return nullptr;
1192 }
1193
1194 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001195 if (scaledStroke.fX * (yRadius * yRadius) <
1196 (scaledStroke.fY * scaledStroke.fY) * xRadius ||
1197 scaledStroke.fY * (xRadius * xRadius) <
1198 (scaledStroke.fX * scaledStroke.fX) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001199 return nullptr;
1200 }
1201
1202 // this is legit only if scale & translation (which should be the case at the moment)
1203 if (isStrokeOnly) {
1204 innerXRadius = xRadius - scaledStroke.fX;
1205 innerYRadius = yRadius - scaledStroke.fY;
1206 }
1207
1208 xRadius += scaledStroke.fX;
1209 yRadius += scaledStroke.fY;
1210 }
1211
Brian Salomon289e3d82016-12-14 15:52:56 -05001212 sk_sp<EllipseOp> op(new EllipseOp());
1213 op->fGeoData.emplace_back(
1214 Geometry{color, xRadius, yRadius, innerXRadius, innerYRadius,
1215 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1216 center.fX + xRadius, center.fY + yRadius)});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001217
Brian Salomon289e3d82016-12-14 15:52:56 -05001218 op->setBounds(op->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07001219
bsalomon4b4a7cc2016-07-08 04:42:54 -07001220 // Outset bounds to include half-pixel width antialiasing.
Brian Salomon289e3d82016-12-14 15:52:56 -05001221 op->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001222
Brian Salomon289e3d82016-12-14 15:52:56 -05001223 op->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1224 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
1225 return std::move(op);
bsalomoncdaa97b2016-03-08 08:30:14 -08001226 }
joshualitt76e7fb62015-02-11 08:52:27 -08001227
Brian Salomon289e3d82016-12-14 15:52:56 -05001228 const char* name() const override { return "EllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001229
Brian Salomon7c3e7182016-12-01 09:35:30 -05001230 SkString dumpInfo() const override {
1231 SkString string;
1232 string.appendf("Stroked: %d\n", fStroked);
1233 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001234 string.appendf(
1235 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1236 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1237 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1238 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1239 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001240 }
1241 string.append(DumpPipelineInfo(*this->pipeline()));
1242 string.append(INHERITED::dumpInfo());
1243 return string;
1244 }
1245
halcanary9d524f22016-03-29 09:03:52 -07001246 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001247 GrInitInvariantOutput* coverage,
1248 GrBatchToXPOverrides* overrides) const override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001249 // When this is called, there is only one ellipse.
ethannicholasff210322015-11-24 12:10:10 -08001250 color->setKnownFourComponents(fGeoData[0].fColor);
1251 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001252 }
1253
bsalomone46f9fe2015-08-18 06:05:14 -07001254private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001255 EllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001256
ethannicholasff210322015-11-24 12:10:10 -08001257 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001258 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001259 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001260 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001261 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001262 if (!overrides.readsLocalCoords()) {
1263 fViewMatrixIfUsingLocalCoords.reset();
1264 }
joshualitt76e7fb62015-02-11 08:52:27 -08001265 }
1266
joshualitt144c3c82015-11-30 12:30:13 -08001267 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001268 SkMatrix localMatrix;
1269 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001270 return;
1271 }
1272
1273 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001274 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001275
joshualitt76e7fb62015-02-11 08:52:27 -08001276 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001277 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001278 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001279 SkASSERT(vertexStride == sizeof(EllipseVertex));
Brian Salomon289e3d82016-12-14 15:52:56 -05001280 EllipseVertex* verts =
1281 reinterpret_cast<EllipseVertex*>(helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001282 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001283 return;
1284 }
1285
bsalomon8415abe2015-05-04 11:41:41 -07001286 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001287 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001288
brianosmanbb2ff942016-02-11 14:15:18 -08001289 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001290 SkScalar xRadius = geom.fXRadius;
1291 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001292
1293 // Compute the reciprocals of the radii here to save time in the shader
1294 SkScalar xRadRecip = SkScalarInvert(xRadius);
1295 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001296 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1297 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001298
bsalomonb5238a72015-05-05 07:49:49 -07001299 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001300
vjiaoblack977996d2016-06-30 12:20:54 -07001301 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1302 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1303 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1304
joshualitt76e7fb62015-02-11 08:52:27 -08001305 // The inner radius in the vertex data must be specified in normalized space.
Brian Salomon289e3d82016-12-14 15:52:56 -05001306 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001307 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001308 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001309 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1310 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1311
Brian Salomon289e3d82016-12-14 15:52:56 -05001312 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001313 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001314 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001315 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1316 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1317
1318 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001319 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001320 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001321 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1322 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1323
1324 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001325 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001326 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001327 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1328 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1329
bsalomonb5238a72015-05-05 07:49:49 -07001330 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001331 }
Hal Canary144caf52016-11-07 17:57:18 -05001332 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001333 }
1334
Brian Salomon25a88092016-12-01 09:36:50 -05001335 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001336 EllipseOp* that = t->cast<EllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001337
1338 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1339 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001340 return false;
1341 }
1342
bsalomoncdaa97b2016-03-08 08:30:14 -08001343 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001344 return false;
1345 }
1346
bsalomoncdaa97b2016-03-08 08:30:14 -08001347 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001348 return false;
1349 }
1350
bsalomoncdaa97b2016-03-08 08:30:14 -08001351 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001352 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001353 return true;
1354 }
1355
bsalomon4b4a7cc2016-07-08 04:42:54 -07001356 struct Geometry {
1357 GrColor fColor;
1358 SkScalar fXRadius;
1359 SkScalar fYRadius;
1360 SkScalar fInnerXRadius;
1361 SkScalar fInnerYRadius;
1362 SkRect fDevBounds;
1363 };
joshualitt76e7fb62015-02-11 08:52:27 -08001364
Brian Salomon289e3d82016-12-14 15:52:56 -05001365 bool fStroked;
1366 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001367 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001368
Brian Salomondad29232016-12-01 16:40:24 -05001369 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001370};
1371
joshualitt76e7fb62015-02-11 08:52:27 -08001372/////////////////////////////////////////////////////////////////////////////////////////////////
1373
Brian Salomon289e3d82016-12-14 15:52:56 -05001374class DIEllipseOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001375public:
Brian Salomon25a88092016-12-01 09:36:50 -05001376 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001377
Brian Salomon289e3d82016-12-14 15:52:56 -05001378 static sk_sp<GrDrawOp> Make(GrColor color,
1379 const SkMatrix& viewMatrix,
1380 const SkRect& ellipse,
1381 const SkStrokeRec& stroke) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001382 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1383 SkScalar xRadius = SkScalarHalf(ellipse.width());
1384 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001385
bsalomon4b4a7cc2016-07-08 04:42:54 -07001386 SkStrokeRec::Style style = stroke.getStyle();
Brian Salomon289e3d82016-12-14 15:52:56 -05001387 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style)
1388 ? DIEllipseStyle::kStroke
1389 : (SkStrokeRec::kHairline_Style == style)
1390 ? DIEllipseStyle::kHairline
1391 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001392
1393 SkScalar innerXRadius = 0;
1394 SkScalar innerYRadius = 0;
1395 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1396 SkScalar strokeWidth = stroke.getWidth();
1397
1398 if (SkScalarNearlyZero(strokeWidth)) {
1399 strokeWidth = SK_ScalarHalf;
1400 } else {
1401 strokeWidth *= SK_ScalarHalf;
1402 }
1403
1404 // we only handle thick strokes for near-circular ellipses
1405 if (strokeWidth > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05001406 (SK_ScalarHalf * xRadius > yRadius || SK_ScalarHalf * yRadius > xRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001407 return nullptr;
1408 }
1409
1410 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05001411 if (strokeWidth * (yRadius * yRadius) < (strokeWidth * strokeWidth) * xRadius ||
1412 strokeWidth * (xRadius * xRadius) < (strokeWidth * strokeWidth) * yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001413 return nullptr;
1414 }
1415
1416 // set inner radius (if needed)
1417 if (SkStrokeRec::kStroke_Style == style) {
1418 innerXRadius = xRadius - strokeWidth;
1419 innerYRadius = yRadius - strokeWidth;
1420 }
1421
1422 xRadius += strokeWidth;
1423 yRadius += strokeWidth;
1424 }
1425 if (DIEllipseStyle::kStroke == dieStyle) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001426 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle::kStroke
1427 : DIEllipseStyle::kFill;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001428 }
1429
1430 // This expands the outer rect so that after CTM we end up with a half-pixel border
1431 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1432 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1433 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1434 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
Brian Salomon289e3d82016-12-14 15:52:56 -05001435 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1436 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001437
Brian Salomon289e3d82016-12-14 15:52:56 -05001438 sk_sp<DIEllipseOp> op(new DIEllipseOp());
1439 op->fGeoData.emplace_back(Geometry{
1440 viewMatrix, color, xRadius, yRadius, innerXRadius, innerYRadius, geoDx, geoDy,
1441 dieStyle,
1442 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1443 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)});
1444 op->setTransformedBounds(op->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1445 IsZeroArea::kNo);
1446 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08001447 }
1448
Brian Salomon289e3d82016-12-14 15:52:56 -05001449 const char* name() const override { return "DIEllipseOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001450
Brian Salomon7c3e7182016-12-01 09:35:30 -05001451 SkString dumpInfo() const override {
1452 SkString string;
1453 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001454 string.appendf(
1455 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1456 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1457 "GeoDY: %.2f\n",
1458 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1459 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1460 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
Brian Salomon7c3e7182016-12-01 09:35:30 -05001461 }
1462 string.append(DumpPipelineInfo(*this->pipeline()));
1463 string.append(INHERITED::dumpInfo());
1464 return string;
1465 }
1466
halcanary9d524f22016-03-29 09:03:52 -07001467 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001468 GrInitInvariantOutput* coverage,
1469 GrBatchToXPOverrides* overrides) const override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001470 // When this is called there is only one ellipse.
ethannicholasff210322015-11-24 12:10:10 -08001471 color->setKnownFourComponents(fGeoData[0].fColor);
1472 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001473 }
1474
bsalomone46f9fe2015-08-18 06:05:14 -07001475private:
Brian Salomon289e3d82016-12-14 15:52:56 -05001476 DIEllipseOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07001477
ethannicholasff210322015-11-24 12:10:10 -08001478 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001479 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001480 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001481 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001482 }
1483
joshualitt144c3c82015-11-30 12:30:13 -08001484 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001485 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001486 sk_sp<GrGeometryProcessor> gp(
1487 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001488
joshualitt76e7fb62015-02-11 08:52:27 -08001489 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001490 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001491 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001492 QuadHelper helper;
1493 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05001494 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001495 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001496 return;
1497 }
1498
joshualitt76e7fb62015-02-11 08:52:27 -08001499 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001500 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001501
brianosmanbb2ff942016-02-11 14:15:18 -08001502 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001503 SkScalar xRadius = geom.fXRadius;
1504 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001505
bsalomonb5238a72015-05-05 07:49:49 -07001506 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001507
1508 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001509 SkScalar offsetDx = geom.fGeoDx / xRadius;
1510 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001511
reed80ea19c2015-05-12 10:37:34 -07001512 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1513 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001514
1515 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001516 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001517 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1518 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1519
Brian Salomon289e3d82016-12-14 15:52:56 -05001520 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001521 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001522 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1523 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1524
1525 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001526 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001527 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1528 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1529
1530 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001531 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001532 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1533 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1534
bsalomonb5238a72015-05-05 07:49:49 -07001535 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001536 }
Hal Canary144caf52016-11-07 17:57:18 -05001537 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001538 }
halcanary9d524f22016-03-29 09:03:52 -07001539
Brian Salomon25a88092016-12-01 09:36:50 -05001540 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001541 DIEllipseOp* that = t->cast<DIEllipseOp>();
bsalomonabd30f52015-08-13 13:34:48 -07001542 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1543 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001544 return false;
1545 }
1546
bsalomoncdaa97b2016-03-08 08:30:14 -08001547 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001548 return false;
1549 }
1550
joshualittd96a67b2015-05-05 14:09:05 -07001551 // TODO rewrite to allow positioning on CPU
1552 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001553 return false;
1554 }
1555
bsalomoncdaa97b2016-03-08 08:30:14 -08001556 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001557 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001558 return true;
1559 }
1560
joshualitt76e7fb62015-02-11 08:52:27 -08001561 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001562 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001563
bsalomon4b4a7cc2016-07-08 04:42:54 -07001564 struct Geometry {
1565 SkMatrix fViewMatrix;
1566 GrColor fColor;
1567 SkScalar fXRadius;
1568 SkScalar fYRadius;
1569 SkScalar fInnerXRadius;
1570 SkScalar fInnerYRadius;
1571 SkScalar fGeoDx;
1572 SkScalar fGeoDy;
1573 DIEllipseStyle fStyle;
1574 SkRect fBounds;
1575 };
1576
Brian Salomon289e3d82016-12-14 15:52:56 -05001577 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001578 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001579
Brian Salomondad29232016-12-01 16:40:24 -05001580 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001581};
1582
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001583///////////////////////////////////////////////////////////////////////////////
1584
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001585// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001586//
1587// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1588// ____________
1589// |_|________|_|
1590// | | | |
1591// | | | |
1592// | | | |
1593// |_|________|_|
1594// |_|________|_|
1595//
1596// For strokes, we don't draw the center quad.
1597//
1598// For circular roundrects, in the case where the stroke width is greater than twice
1599// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001600// in the center. The shared vertices are duplicated so we can set a different outer radius
1601// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001602// ____________
1603// |_|________|_|
1604// | |\ ____ /| |
1605// | | | | | |
1606// | | |____| | |
1607// |_|/______\|_|
1608// |_|________|_|
1609//
1610// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001611//
1612// For filled rrects that need to provide a distance vector we resuse the overstroke
1613// geometry but make the inner rect degenerate (either a point or a horizontal or
1614// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001615
jvanverth84839f62016-08-29 10:16:40 -07001616static const uint16_t gOverstrokeRRectIndices[] = {
Brian Salomon289e3d82016-12-14 15:52:56 -05001617 // clang-format off
1618 // overstroke quads
1619 // we place this at the beginning so that we can skip these indices when rendering normally
1620 16, 17, 19, 16, 19, 18,
1621 19, 17, 23, 19, 23, 21,
1622 21, 23, 22, 21, 22, 20,
1623 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001624
Brian Salomon289e3d82016-12-14 15:52:56 -05001625 // corners
1626 0, 1, 5, 0, 5, 4,
1627 2, 3, 7, 2, 7, 6,
1628 8, 9, 13, 8, 13, 12,
1629 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001630
Brian Salomon289e3d82016-12-14 15:52:56 -05001631 // edges
1632 1, 2, 6, 1, 6, 5,
1633 4, 5, 9, 4, 9, 8,
1634 6, 7, 11, 6, 11, 10,
1635 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001636
Brian Salomon289e3d82016-12-14 15:52:56 -05001637 // center
1638 // we place this at the end so that we can ignore these indices when not rendering as filled
1639 5, 6, 10, 5, 10, 9,
1640 // clang-format on
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001641};
Brian Salomon289e3d82016-12-14 15:52:56 -05001642
jvanverth84839f62016-08-29 10:16:40 -07001643// fill and standard stroke indices skip the overstroke "ring"
Brian Salomon289e3d82016-12-14 15:52:56 -05001644static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001645
jvanverth84839f62016-08-29 10:16:40 -07001646// overstroke count is arraysize minus the center indices
1647static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1648// fill count skips overstroke indices and includes center
Brian Salomon289e3d82016-12-14 15:52:56 -05001649static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001650// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001651static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1652static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001653static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001654
jvanverthc3d0e422016-08-25 08:12:35 -07001655enum RRectType {
1656 kFill_RRectType,
1657 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001658 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001659 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001660};
1661
jvanverth84839f62016-08-29 10:16:40 -07001662static int rrect_type_to_vert_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001663 switch (type) {
1664 case kFill_RRectType:
1665 case kStroke_RRectType:
1666 return kVertsPerStandardRRect;
1667 case kOverstroke_RRectType:
1668 case kFillWithDist_RRectType:
1669 return kVertsPerOverstrokeRRect;
1670 }
1671 SkFAIL("Invalid type");
1672 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001673}
1674
1675static int rrect_type_to_index_count(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001676 switch (type) {
1677 case kFill_RRectType:
1678 return kIndicesPerFillRRect;
1679 case kStroke_RRectType:
1680 return kIndicesPerStrokeRRect;
1681 case kOverstroke_RRectType:
1682 case kFillWithDist_RRectType:
1683 return kIndicesPerOverstrokeRRect;
1684 }
1685 SkFAIL("Invalid type");
1686 return 0;
jvanverth84839f62016-08-29 10:16:40 -07001687}
1688
1689static const uint16_t* rrect_type_to_indices(RRectType type) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001690 switch (type) {
1691 case kFill_RRectType:
1692 case kStroke_RRectType:
1693 return gStandardRRectIndices;
1694 case kOverstroke_RRectType:
1695 case kFillWithDist_RRectType:
1696 return gOverstrokeRRectIndices;
1697 }
1698 SkFAIL("Invalid type");
1699 return 0;
bsalomoned0bcad2015-05-04 10:36:42 -07001700}
1701
joshualitt76e7fb62015-02-11 08:52:27 -08001702///////////////////////////////////////////////////////////////////////////////////////////////////
1703
Robert Phillips79839d42016-10-06 15:03:34 -04001704// For distance computations in the interior of filled rrects we:
1705//
1706// add a interior degenerate (point or line) rect
1707// each vertex of that rect gets -outerRad as its radius
1708// this makes the computation of the distance to the outer edge be negative
1709// negative values are caught and then handled differently in the GP's onEmitCode
1710// each vertex is also given the normalized x & y distance from the interior rect's edge
1711// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1712
Brian Salomon289e3d82016-12-14 15:52:56 -05001713class CircularRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08001714public:
Brian Salomon25a88092016-12-01 09:36:50 -05001715 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07001716
bsalomon4b4a7cc2016-07-08 04:42:54 -07001717 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1718 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05001719 CircularRRectOp(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1720 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1721 : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001722 SkRect bounds = devRect;
1723 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1724 SkScalar innerRadius = 0.0f;
1725 SkScalar outerRadius = devRadius;
1726 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001727 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001728 if (devStrokeWidth > 0) {
1729 if (SkScalarNearlyZero(devStrokeWidth)) {
1730 halfWidth = SK_ScalarHalf;
1731 } else {
1732 halfWidth = SkScalarHalf(devStrokeWidth);
1733 }
joshualitt76e7fb62015-02-11 08:52:27 -08001734
bsalomon4b4a7cc2016-07-08 04:42:54 -07001735 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001736 // Outset stroke by 1/4 pixel
1737 devStrokeWidth += 0.25f;
1738 // If stroke is greater than width or height, this is still a fill
1739 // Otherwise we compute stroke params
Brian Salomon289e3d82016-12-14 15:52:56 -05001740 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
jvanverthc3d0e422016-08-25 08:12:35 -07001741 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001742 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001743 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001744 }
1745 outerRadius += halfWidth;
1746 bounds.outset(halfWidth, halfWidth);
1747 }
Robert Phillips79839d42016-10-06 15:03:34 -04001748 if (kFill_RRectType == type && needsDistance) {
1749 type = kFillWithDist_RRectType;
1750 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001751
bsalomon4b4a7cc2016-07-08 04:42:54 -07001752 // The radii are outset for two reasons. First, it allows the shader to simply perform
1753 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1754 // Second, the outer radius is used to compute the verts of the bounding box that is
1755 // rendered and the outset ensures the box will cover all partially covered by the rrect
1756 // corners.
1757 outerRadius += SK_ScalarHalf;
1758 innerRadius -= SK_ScalarHalf;
1759
bsalomon88cf17d2016-07-08 06:40:56 -07001760 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1761
1762 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001763 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1764
Brian Salomon289e3d82016-12-14 15:52:56 -05001765 fGeoData.emplace_back(Geometry{color, innerRadius, outerRadius, bounds, type});
jvanverth84839f62016-08-29 10:16:40 -07001766 fVertCount = rrect_type_to_vert_count(type);
1767 fIndexCount = rrect_type_to_index_count(type);
1768 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001769 }
1770
Brian Salomon289e3d82016-12-14 15:52:56 -05001771 const char* name() const override { return "CircularRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001772
jvanverthc3d0e422016-08-25 08:12:35 -07001773 SkString dumpInfo() const override {
1774 SkString string;
1775 for (int i = 0; i < fGeoData.count(); ++i) {
Brian Salomon289e3d82016-12-14 15:52:56 -05001776 string.appendf(
1777 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1778 "InnerRad: %.2f, OuterRad: %.2f\n",
1779 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1780 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1781 fGeoData[i].fInnerRadius, fGeoData[i].fOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001782 }
Brian Salomon7c3e7182016-12-01 09:35:30 -05001783 string.append(DumpPipelineInfo(*this->pipeline()));
jvanverthc3d0e422016-08-25 08:12:35 -07001784 string.append(INHERITED::dumpInfo());
1785 return string;
1786 }
1787
halcanary9d524f22016-03-29 09:03:52 -07001788 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001789 GrInitInvariantOutput* coverage,
1790 GrBatchToXPOverrides* overrides) const override {
Brian Salomon289e3d82016-12-14 15:52:56 -05001791 // When this is called there is only one rrect.
ethannicholasff210322015-11-24 12:10:10 -08001792 color->setKnownFourComponents(fGeoData[0].fColor);
1793 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001794 }
1795
bsalomone46f9fe2015-08-18 06:05:14 -07001796private:
ethannicholasff210322015-11-24 12:10:10 -08001797 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001798 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001799 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001800 if (!overrides.readsLocalCoords()) {
1801 fViewMatrixIfUsingLocalCoords.reset();
1802 }
joshualitt76e7fb62015-02-11 08:52:27 -08001803 }
1804
Robert Phillips79839d42016-10-06 15:03:34 -04001805 struct CircleVertex {
Brian Salomon289e3d82016-12-14 15:52:56 -05001806 SkPoint fPos;
1807 GrColor fColor;
1808 SkPoint fOffset;
Robert Phillips79839d42016-10-06 15:03:34 -04001809 SkScalar fOuterRadius;
1810 SkScalar fInnerRadius;
1811 // No half plane, we don't use it here.
1812 };
1813
Brian Salomon289e3d82016-12-14 15:52:56 -05001814 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1815 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1816 SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001817 SkASSERT(smInset < bigInset);
1818
1819 // TL
1820 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1821 (*verts)->fColor = color;
1822 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1823 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001824 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001825 (*verts)++;
1826
1827 // TR
Brian Salomon289e3d82016-12-14 15:52:56 -05001828 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001829 (*verts)->fColor = color;
1830 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1831 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001832 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001833 (*verts)++;
1834
Brian Salomon289e3d82016-12-14 15:52:56 -05001835 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001836 (*verts)->fColor = color;
1837 (*verts)->fOffset = SkPoint::Make(0, 0);
1838 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001839 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001840 (*verts)++;
1841
Brian Salomon289e3d82016-12-14 15:52:56 -05001842 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
Robert Phillips79839d42016-10-06 15:03:34 -04001843 (*verts)->fColor = color;
1844 (*verts)->fOffset = SkPoint::Make(0, 0);
1845 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001846 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001847 (*verts)++;
1848
1849 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1850 (*verts)->fColor = color;
1851 (*verts)->fOffset = SkPoint::Make(0, 0);
1852 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001853 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001854 (*verts)++;
1855
1856 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1857 (*verts)->fColor = color;
1858 (*verts)->fOffset = SkPoint::Make(0, 0);
1859 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001860 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001861 (*verts)++;
1862
1863 // BL
1864 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1865 (*verts)->fColor = color;
1866 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1867 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001868 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001869 (*verts)++;
1870
1871 // BR
1872 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1873 (*verts)->fColor = color;
1874 (*verts)->fOffset = SkPoint::Make(xOffset, 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 }
1879
joshualitt144c3c82015-11-30 12:30:13 -08001880 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001881 // Invert the view matrix as a local matrix (if any other processors require coords).
1882 SkMatrix localMatrix;
1883 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001884 return;
1885 }
1886
1887 // Setup geometry processor
Brian Salomon289e3d82016-12-14 15:52:56 -05001888 sk_sp<GrGeometryProcessor> gp(
1889 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001890
joshualitt76e7fb62015-02-11 08:52:27 -08001891 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001892 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001893 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001894
jvanverth84839f62016-08-29 10:16:40 -07001895 const GrBuffer* vertexBuffer;
1896 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001897
Brian Salomon289e3d82016-12-14 15:52:56 -05001898 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1899 &vertexBuffer, &firstVertex);
jvanverth84839f62016-08-29 10:16:40 -07001900 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001901 SkDebugf("Could not allocate vertices\n");
1902 return;
1903 }
1904
jvanverth84839f62016-08-29 10:16:40 -07001905 const GrBuffer* indexBuffer = nullptr;
1906 int firstIndex = 0;
1907 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1908 if (!indices) {
1909 SkDebugf("Could not allocate indices\n");
1910 return;
1911 }
1912
1913 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001914 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001915 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001916
brianosmanbb2ff942016-02-11 14:15:18 -08001917 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001918 SkScalar outerRadius = args.fOuterRadius;
1919
egdanielbc227142015-04-21 06:28:08 -07001920 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001921
Brian Salomon289e3d82016-12-14 15:52:56 -05001922 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1923 bounds.fBottom - outerRadius, bounds.fBottom};
joshualitt76e7fb62015-02-11 08:52:27 -08001924
Brian Salomon289e3d82016-12-14 15:52:56 -05001925 SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
joshualitt76e7fb62015-02-11 08:52:27 -08001926 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001927 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Brian Salomon289e3d82016-12-14 15:52:56 -05001928 SkScalar innerRadius =
1929 args.fType != kFill_RRectType && args.fType != kFillWithDist_RRectType
1930 ? args.fInnerRadius / args.fOuterRadius
1931 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001932 for (int i = 0; i < 4; ++i) {
1933 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001934 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001935 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1936 verts->fOuterRadius = outerRadius;
1937 verts->fInnerRadius = innerRadius;
1938 verts++;
1939
1940 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001941 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001942 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1943 verts->fOuterRadius = outerRadius;
1944 verts->fInnerRadius = innerRadius;
1945 verts++;
1946
1947 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001948 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001949 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1950 verts->fOuterRadius = outerRadius;
1951 verts->fInnerRadius = innerRadius;
1952 verts++;
1953
1954 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001955 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001956 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1957 verts->fOuterRadius = outerRadius;
1958 verts->fInnerRadius = innerRadius;
1959 verts++;
1960 }
jvanverthc3d0e422016-08-25 08:12:35 -07001961 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001962 // Effectively this is an additional stroked rrect, with its
1963 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1964 // This will give us correct AA in the center and the correct
1965 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001966 //
jvanvertha4f1af82016-08-29 07:17:47 -07001967 // Also, the outer offset is a constant vector pointing to the right, which
1968 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001969 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001970 SkASSERT(args.fInnerRadius <= 0.0f);
1971
jvanvertha4f1af82016-08-29 07:17:47 -07001972 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1973 // this is the normalized distance from the outer rectangle of this
1974 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001975 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001976
Brian Salomon289e3d82016-12-14 15:52:56 -05001977 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1978 overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001979 }
jvanverth6a397612016-08-26 08:15:33 -07001980
Robert Phillips79839d42016-10-06 15:03:34 -04001981 if (kFillWithDist_RRectType == args.fType) {
1982 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001983
Robert Phillips79839d42016-10-06 15:03:34 -04001984 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001985
Brian Salomon289e3d82016-12-14 15:52:56 -05001986 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
1987 -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001988 }
jvanverth84839f62016-08-29 10:16:40 -07001989
1990 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1991 const int primIndexCount = rrect_type_to_index_count(args.fType);
1992 for (int i = 0; i < primIndexCount; ++i) {
1993 *indices++ = primIndices[i] + currStartVertex;
1994 }
1995
1996 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001997 }
1998
jvanverth84839f62016-08-29 10:16:40 -07001999 GrMesh mesh;
2000 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
2001 firstIndex, fVertCount, fIndexCount);
2002 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08002003 }
2004
Brian Salomon25a88092016-12-01 09:36:50 -05002005 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002006 CircularRRectOp* that = t->cast<CircularRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002007 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2008 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002009 return false;
2010 }
2011
bsalomoncdaa97b2016-03-08 08:30:14 -08002012 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002013 return false;
2014 }
2015
bsalomoncdaa97b2016-03-08 08:30:14 -08002016 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002017 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07002018 fVertCount += that->fVertCount;
2019 fIndexCount += that->fIndexCount;
2020 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002021 return true;
2022 }
2023
bsalomon4b4a7cc2016-07-08 04:42:54 -07002024 struct Geometry {
Brian Salomon289e3d82016-12-14 15:52:56 -05002025 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002026 SkScalar fInnerRadius;
2027 SkScalar fOuterRadius;
2028 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002029 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002030 };
2031
joshualitt76e7fb62015-02-11 08:52:27 -08002032 SkSTArray<1, Geometry, true> fGeoData;
Brian Salomon289e3d82016-12-14 15:52:56 -05002033 SkMatrix fViewMatrixIfUsingLocalCoords;
2034 int fVertCount;
2035 int fIndexCount;
2036 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002037
Brian Salomondad29232016-12-01 16:40:24 -05002038 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002039};
2040
jvanverth84839f62016-08-29 10:16:40 -07002041static const int kNumRRectsInIndexBuffer = 256;
2042
2043GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2044GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2045static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2046 GrResourceProvider* resourceProvider) {
2047 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2048 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2049 switch (type) {
2050 case kFill_RRectType:
2051 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002052 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2053 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002054 case kStroke_RRectType:
2055 return resourceProvider->findOrCreateInstancedIndexBuffer(
Brian Salomon289e3d82016-12-14 15:52:56 -05002056 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2057 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
jvanverth84839f62016-08-29 10:16:40 -07002058 default:
2059 SkASSERT(false);
2060 return nullptr;
2061 };
2062}
2063
Brian Salomon289e3d82016-12-14 15:52:56 -05002064class EllipticalRRectOp : public GrMeshDrawOp {
joshualitt76e7fb62015-02-11 08:52:27 -08002065public:
Brian Salomon25a88092016-12-01 09:36:50 -05002066 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -07002067
bsalomon4b4a7cc2016-07-08 04:42:54 -07002068 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2069 // whether the rrect is only stroked or stroked and filled.
Brian Salomon289e3d82016-12-14 15:52:56 -05002070 static sk_sp<GrDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
2071 float devXRadius, float devYRadius, SkVector devStrokeWidths,
2072 bool strokeOnly) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002073 SkASSERT(devXRadius > 0.5);
2074 SkASSERT(devYRadius > 0.5);
2075 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2076 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2077 SkScalar innerXRadius = 0.0f;
2078 SkScalar innerYRadius = 0.0f;
2079 SkRect bounds = devRect;
2080 bool stroked = false;
2081 if (devStrokeWidths.fX > 0) {
2082 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2083 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2084 } else {
2085 devStrokeWidths.scale(SK_ScalarHalf);
2086 }
joshualitt76e7fb62015-02-11 08:52:27 -08002087
bsalomon4b4a7cc2016-07-08 04:42:54 -07002088 // we only handle thick strokes for near-circular ellipses
2089 if (devStrokeWidths.length() > SK_ScalarHalf &&
Brian Salomon289e3d82016-12-14 15:52:56 -05002090 (SK_ScalarHalf * devXRadius > devYRadius ||
2091 SK_ScalarHalf * devYRadius > devXRadius)) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002092 return nullptr;
2093 }
2094
2095 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
Brian Salomon289e3d82016-12-14 15:52:56 -05002096 if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2097 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002098 return nullptr;
2099 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002100 if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2101 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002102 return nullptr;
2103 }
2104
2105 // this is legit only if scale & translation (which should be the case at the moment)
2106 if (strokeOnly) {
2107 innerXRadius = devXRadius - devStrokeWidths.fX;
2108 innerYRadius = devYRadius - devStrokeWidths.fY;
2109 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2110 }
2111
2112 devXRadius += devStrokeWidths.fX;
2113 devYRadius += devStrokeWidths.fY;
2114 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2115 }
2116
Brian Salomon289e3d82016-12-14 15:52:56 -05002117 sk_sp<EllipticalRRectOp> op(new EllipticalRRectOp());
2118 op->fStroked = stroked;
2119 op->fViewMatrixIfUsingLocalCoords = viewMatrix;
2120 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
bsalomon88cf17d2016-07-08 06:40:56 -07002121 // Expand the rect for aa in order to generate the correct vertices.
2122 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
Brian Salomon289e3d82016-12-14 15:52:56 -05002123 op->fGeoData.emplace_back(
2124 Geometry{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2125 return std::move(op);
joshualitt76e7fb62015-02-11 08:52:27 -08002126 }
2127
Brian Salomon289e3d82016-12-14 15:52:56 -05002128 const char* name() const override { return "EllipticalRRectOp"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002129
Brian Salomon7c3e7182016-12-01 09:35:30 -05002130 SkString dumpInfo() const override {
2131 SkString string;
2132 string.appendf("Stroked: %d\n", fStroked);
2133 for (const auto& geo : fGeoData) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002134 string.appendf(
2135 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2136 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2137 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2138 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2139 geo.fInnerYRadius);
Brian Salomon7c3e7182016-12-01 09:35:30 -05002140 }
2141 string.append(DumpPipelineInfo(*this->pipeline()));
2142 string.append(INHERITED::dumpInfo());
2143 return string;
2144 }
2145
halcanary9d524f22016-03-29 09:03:52 -07002146 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08002147 GrInitInvariantOutput* coverage,
2148 GrBatchToXPOverrides* overrides) const override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002149 // When this is called there is only one rrect.
ethannicholasff210322015-11-24 12:10:10 -08002150 color->setKnownFourComponents(fGeoData[0].fColor);
2151 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08002152 }
2153
bsalomone46f9fe2015-08-18 06:05:14 -07002154private:
Brian Salomon289e3d82016-12-14 15:52:56 -05002155 EllipticalRRectOp() : INHERITED(ClassID()) {}
bsalomon4b4a7cc2016-07-08 04:42:54 -07002156
ethannicholasff210322015-11-24 12:10:10 -08002157 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002158 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08002159 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08002160 if (!overrides.readsLocalCoords()) {
2161 fViewMatrixIfUsingLocalCoords.reset();
2162 }
joshualitt76e7fb62015-02-11 08:52:27 -08002163 }
2164
joshualitt144c3c82015-11-30 12:30:13 -08002165 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002166 SkMatrix localMatrix;
2167 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002168 return;
2169 }
2170
2171 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002172 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002173
joshualitt76e7fb62015-02-11 08:52:27 -08002174 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002175 size_t vertexStride = gp->getVertexStride();
2176 SkASSERT(vertexStride == sizeof(EllipseVertex));
2177
bsalomonb5238a72015-05-05 07:49:49 -07002178 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002179 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Brian Salomon289e3d82016-12-14 15:52:56 -05002180 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2181 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002182
bsalomonb5238a72015-05-05 07:49:49 -07002183 InstancedHelper helper;
2184 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Brian Salomon289e3d82016-12-14 15:52:56 -05002185 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
2186 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002187 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002188 SkDebugf("Could not allocate vertices\n");
2189 return;
2190 }
2191
joshualitt76e7fb62015-02-11 08:52:27 -08002192 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002193 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002194
brianosmanbb2ff942016-02-11 14:15:18 -08002195 GrColor color = args.fColor;
2196
joshualitt76e7fb62015-02-11 08:52:27 -08002197 // Compute the reciprocals of the radii here to save time in the shader
2198 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2199 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2200 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2201 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2202
2203 // Extend the radii out half a pixel to antialias.
2204 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2205 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2206
egdanielbc227142015-04-21 06:28:08 -07002207 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002208
Brian Salomon289e3d82016-12-14 15:52:56 -05002209 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2210 bounds.fBottom - yOuterRadius, bounds.fBottom};
2211 SkScalar yOuterOffsets[4] = {yOuterRadius,
2212 SK_ScalarNearlyZero, // we're using inversesqrt() in
2213 // shader, so can't be exactly 0
2214 SK_ScalarNearlyZero, yOuterRadius};
joshualitt76e7fb62015-02-11 08:52:27 -08002215
2216 for (int i = 0; i < 4; ++i) {
2217 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002218 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002219 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2220 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2221 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2222 verts++;
2223
2224 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002225 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002226 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2227 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2228 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2229 verts++;
2230
2231 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002232 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002233 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2234 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2235 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2236 verts++;
2237
2238 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002239 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002240 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2241 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2242 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2243 verts++;
2244 }
2245 }
Hal Canary144caf52016-11-07 17:57:18 -05002246 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08002247 }
2248
Brian Salomon25a88092016-12-01 09:36:50 -05002249 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Brian Salomon289e3d82016-12-14 15:52:56 -05002250 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
bsalomonabd30f52015-08-13 13:34:48 -07002251
2252 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2253 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002254 return false;
2255 }
2256
bsalomoncdaa97b2016-03-08 08:30:14 -08002257 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002258 return false;
2259 }
2260
bsalomoncdaa97b2016-03-08 08:30:14 -08002261 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002262 return false;
2263 }
2264
bsalomoncdaa97b2016-03-08 08:30:14 -08002265 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002266 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002267 return true;
2268 }
2269
bsalomon4b4a7cc2016-07-08 04:42:54 -07002270 struct Geometry {
2271 GrColor fColor;
2272 SkScalar fXRadius;
2273 SkScalar fYRadius;
2274 SkScalar fInnerXRadius;
2275 SkScalar fInnerYRadius;
2276 SkRect fDevBounds;
2277 };
2278
Brian Salomon289e3d82016-12-14 15:52:56 -05002279 bool fStroked;
2280 SkMatrix fViewMatrixIfUsingLocalCoords;
2281 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002282
Brian Salomondad29232016-12-01 16:40:24 -05002283 typedef GrMeshDrawOp INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002284};
2285
Brian Salomon289e3d82016-12-14 15:52:56 -05002286static sk_sp<GrDrawOp> make_rrect_op(GrColor color,
2287 bool needsDistance,
2288 const SkMatrix& viewMatrix,
2289 const SkRRect& rrect,
2290 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002291 SkASSERT(viewMatrix.rectStaysRect());
2292 SkASSERT(rrect.isSimple());
2293 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002294
joshualitt3e708c52015-04-30 13:49:27 -07002295 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002296 // do any matrix crunching before we reset the draw state for device coords
2297 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002298 SkRect bounds;
2299 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002300
2301 SkVector radii = rrect.getSimpleRadii();
Brian Salomon289e3d82016-12-14 15:52:56 -05002302 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2303 viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2304 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2305 viewMatrix[SkMatrix::kMScaleY] * radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002306
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002307 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002308
bsalomon4b4a7cc2016-07-08 04:42:54 -07002309 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2310 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002311 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002312
Brian Salomon289e3d82016-12-14 15:52:56 -05002313 bool isStrokeOnly =
2314 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002315 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2316
jvanverthc3d0e422016-08-25 08:12:35 -07002317 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002318 if (hasStroke) {
2319 if (SkStrokeRec::kHairline_Style == style) {
2320 scaledStroke.set(1, 1);
2321 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002322 scaledStroke.fX = SkScalarAbs(
2323 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2324 scaledStroke.fY = SkScalarAbs(
2325 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002326 }
2327
jvanverthc3d0e422016-08-25 08:12:35 -07002328 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2329 // for non-circular rrects, if half of strokewidth is greater than radius,
2330 // we don't handle that right now
Brian Salomon289e3d82016-12-14 15:52:56 -05002331 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2332 SK_ScalarHalf * scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002333 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002334 }
2335 }
2336
2337 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2338 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2339 // patch will have fractional coverage. This only matters when the interior is actually filled.
2340 // We could consider falling back to rect rendering here, since a tiny radius is
2341 // indistinguishable from a square corner.
2342 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002343 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002344 }
2345
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002346 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002347 if (isCircular) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002348 return sk_sp<GrDrawOp>(new CircularRRectOp(color, needsDistance, viewMatrix, bounds,
2349 xRadius, scaledStroke.fX, isStrokeOnly));
2350 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002351 } else {
Brian Salomon289e3d82016-12-14 15:52:56 -05002352 return EllipticalRRectOp::Make(color, viewMatrix, bounds, xRadius, yRadius, scaledStroke,
2353 isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002354 }
joshualitt3e708c52015-04-30 13:49:27 -07002355}
2356
Brian Salomon289e3d82016-12-14 15:52:56 -05002357sk_sp<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrColor color,
2358 bool needsDistance,
2359 const SkMatrix& viewMatrix,
2360 const SkRRect& rrect,
2361 const SkStrokeRec& stroke,
2362 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002363 if (rrect.isOval()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002364 return MakeOvalOp(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002365 }
2366
2367 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002368 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002369 }
2370
Brian Salomon289e3d82016-12-14 15:52:56 -05002371 return make_rrect_op(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002372}
joshualitt3e708c52015-04-30 13:49:27 -07002373
bsalomon4b4a7cc2016-07-08 04:42:54 -07002374///////////////////////////////////////////////////////////////////////////////
2375
Brian Salomon289e3d82016-12-14 15:52:56 -05002376sk_sp<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrColor color,
2377 const SkMatrix& viewMatrix,
2378 const SkRect& oval,
2379 const SkStrokeRec& stroke,
2380 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002381 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002382 SkScalar width = oval.width();
2383 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2384 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002385 return CircleOp::Make(color, viewMatrix, center, width / 2.f, GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002386 }
2387
2388 // if we have shader derivative support, render as device-independent
2389 if (shaderCaps->shaderDerivativeSupport()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002390 return DIEllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002391 }
2392
2393 // otherwise axis-aligned ellipses only
2394 if (viewMatrix.rectStaysRect()) {
Brian Salomon289e3d82016-12-14 15:52:56 -05002395 return EllipseOp::Make(color, viewMatrix, oval, stroke);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002396 }
2397
2398 return nullptr;
2399}
2400
2401///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002402
Brian Salomon289e3d82016-12-14 15:52:56 -05002403sk_sp<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrColor color, const SkMatrix& viewMatrix,
2404 const SkRect& oval, SkScalar startAngle,
2405 SkScalar sweepAngle, bool useCenter,
2406 const GrStyle& style, const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002407 SkASSERT(!oval.isEmpty());
2408 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002409 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002410 if (SkScalarAbs(sweepAngle) >= 360.f) {
2411 return nullptr;
2412 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002413 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2414 return nullptr;
2415 }
2416 SkPoint center = {oval.centerX(), oval.centerY()};
Brian Salomon289e3d82016-12-14 15:52:56 -05002417 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2418 useCenter};
2419 return CircleOp::Make(color, viewMatrix, center, width / 2.f, style, &arcParams);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002420}
2421
2422///////////////////////////////////////////////////////////////////////////////
2423
joshualitt3e708c52015-04-30 13:49:27 -07002424#ifdef GR_TEST_UTILS
2425
Brian Salomon289e3d82016-12-14 15:52:56 -05002426DRAW_BATCH_TEST_DEFINE(CircleOp) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002427 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002428 SkScalar rotate = random->nextSScalar1() * 360.f;
2429 SkScalar translateX = random->nextSScalar1() * 1000.f;
2430 SkScalar translateY = random->nextSScalar1() * 1000.f;
2431 SkScalar scale = random->nextSScalar1() * 100.f;
2432 SkMatrix viewMatrix;
2433 viewMatrix.setRotate(rotate);
2434 viewMatrix.postTranslate(translateX, translateY);
2435 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002436 GrColor color = GrRandomColor(random);
2437 SkRect circle = GrTest::TestSquare(random);
2438 SkPoint center = {circle.centerX(), circle.centerY()};
2439 SkScalar radius = circle.width() / 2.f;
2440 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002441 CircleOp::ArcParams arcParamsTmp;
2442 const CircleOp::ArcParams* arcParams = nullptr;
bsalomon4f3a0ca2016-08-22 13:14:26 -07002443 if (random->nextBool()) {
2444 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002445 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2446 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002447 arcParams = &arcParamsTmp;
2448 }
Brian Salomon289e3d82016-12-14 15:52:56 -05002449 sk_sp<GrDrawOp> op = CircleOp::Make(color, viewMatrix, center, radius,
2450 GrStyle(stroke, nullptr), arcParams);
2451 if (op) {
2452 return op.release();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002453 }
2454 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002455}
2456
Brian Salomon289e3d82016-12-14 15:52:56 -05002457DRAW_BATCH_TEST_DEFINE(EllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002458 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2459 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002460 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002461 return EllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)).release();
joshualitt3e708c52015-04-30 13:49:27 -07002462}
2463
Brian Salomon289e3d82016-12-14 15:52:56 -05002464DRAW_BATCH_TEST_DEFINE(DIEllipseOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002465 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2466 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002467 SkRect ellipse = GrTest::TestSquare(random);
Brian Salomon289e3d82016-12-14 15:52:56 -05002468 return DIEllipseOp::Make(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random)).release();
joshualitt3e708c52015-04-30 13:49:27 -07002469}
2470
Brian Salomon289e3d82016-12-14 15:52:56 -05002471DRAW_BATCH_TEST_DEFINE(RRectOp) {
joshualitt3e708c52015-04-30 13:49:27 -07002472 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2473 GrColor color = GrRandomColor(random);
2474 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002475 bool needsDistance = random->nextBool();
Brian Salomon289e3d82016-12-14 15:52:56 -05002476 return make_rrect_op(color, needsDistance, viewMatrix, rrect, GrTest::TestStrokeRec(random))
2477 .release();
joshualitt3e708c52015-04-30 13:49:27 -07002478}
2479
2480#endif