blob: c57edc9fc0c54f95c342aebcdd0484886da650d1 [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
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080012#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080013#include "GrInvariantOutput.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080023#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080026#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080034 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080042 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080060 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080061 * vec4f : (p.xy, outerRad, innerRad)
62 * p is the position in the normalized space.
63 * outerRad is the outerRadius in device space.
64 * innerRad is the innerRadius in normalized space (ignored if not stroking).
jvanverth6c177a12016-08-17 07:59:41 -070065 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
66 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
67 * vec4f : (v.xy, outerDistance, innerDistance)
68 * v is a normalized vector pointing to the outer edge
69 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
70 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070071 * Additional clip planes are supported for rendering circular arcs. The additional planes are
72 * either intersected or unioned together. Up to three planes are supported (an initial plane,
73 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
74 * are useful for any given arc, but having all three in one instance allows batching different
75 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 */
77
bsalomoncdaa97b2016-03-08 08:30:14 -080078class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070080 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
81 const SkMatrix& localMatrix)
82 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080083 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070084 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
87 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 if (clipPlane) {
89 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInClipPlane = nullptr;
92 }
93 if (isectPlane) {
94 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
95 } else {
96 fInIsectPlane = nullptr;
97 }
98 if (unionPlane) {
99 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
100 } else {
101 fInUnionPlane = nullptr;
102 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800103 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 }
105
bsalomon4f3a0ca2016-08-22 13:14:26 -0700106 bool implementsDistanceVector() const override { return !fInClipPlane; };
dvonbeck68f2f7d2016-08-01 11:37:45 -0700107
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
mtklein36352bf2015-03-25 18:17:31 -0700110 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
bsalomon31df31c2016-08-17 09:00:24 -0700112 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
113 GLSLProcessor::GenKey(*this, caps, b);
114 }
115
116 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
117 return new GLSLProcessor();
118 }
119
120private:
egdaniel57d3b032015-11-13 11:57:27 -0800121 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800123 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124
mtklein36352bf2015-03-25 18:17:31 -0700125 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800127 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800128 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800129 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800131
joshualittabb52a12015-01-13 15:02:10 -0800132 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800133 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 fragBuilder->codeAppend("vec4 circleEdge;");
135 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800164 args.fTransformsIn,
165 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800166
bsalomon4f3a0ca2016-08-22 13:14:26 -0700167 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
168 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
169 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800170 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700171 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700172 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800173 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000175
dvonbeck68f2f7d2016-08-01 11:37:45 -0700176 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700177 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700178 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700179 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700180 "%s);", // no normalize
181 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700182 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700183 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
184 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700185 fragBuilder->codeAppend ("}");
186 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 if (cgp.fInClipPlane) {
188 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
189 if (cgp.fInIsectPlane) {
190 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
191 }
192 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700193 fragBuilder->codeAppend("clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700194 }
195 fragBuilder->codeAppend("edgeAlpha *= clip;");
196 }
egdaniel4ca2e602015-11-18 08:01:26 -0800197 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198 }
199
robertphillips46d36f02015-01-18 08:14:14 -0800200 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700201 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700202 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800203 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700204 uint16_t key;
205 key = cgp.fStroke ? 0x01 : 0x0;
206 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
207 key |= cgp.fInClipPlane ? 0x04 : 0x0;
208 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
209 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700210 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211 }
212
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214
joshualitte3ababe2015-05-15 07:56:07 -0700215 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700216 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700217 int index,
218 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomone4f24612016-08-17 10:30:17 -0700219 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
220 pdman, index, transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700221 }
222
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 private:
egdaniele659a582015-11-13 09:55:43 -0800224 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 };
226
bsalomoncdaa97b2016-03-08 08:30:14 -0800227 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800228 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800229 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800230 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700231 const Attribute* fInClipPlane;
232 const Attribute* fInIsectPlane;
233 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800234 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235
joshualittb0a8a372014-09-23 09:50:21 -0700236 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237
joshualitt249af152014-09-15 11:41:13 -0700238 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239};
240
bsalomoncdaa97b2016-03-08 08:30:14 -0800241GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242
bungeman06ca8ec2016-06-09 08:01:03 -0700243sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
244 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700245 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
246 d->fRandom->nextBool(), d->fRandom->nextBool(),
247 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248}
249
250///////////////////////////////////////////////////////////////////////////////
251
252/**
253 * 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 +0000254 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
255 * in both x and y directions.
256 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000257 * 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 +0000258 */
259
bsalomoncdaa97b2016-03-08 08:30:14 -0800260class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800262 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
263 : fLocalMatrix(localMatrix) {
264 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700265 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
266 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
267 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
268 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800269 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000270 }
271
bsalomoncdaa97b2016-03-08 08:30:14 -0800272 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
mtklein36352bf2015-03-25 18:17:31 -0700274 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800275
bsalomon31df31c2016-08-17 09:00:24 -0700276 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
277 GLSLProcessor::GenKey(*this, caps, b);
278 }
279
280 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
281 return new GLSLProcessor();
282 }
283
284private:
egdaniel57d3b032015-11-13 11:57:27 -0800285 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000286 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800287 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000288
mtklein36352bf2015-03-25 18:17:31 -0700289 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800290 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800291 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800292 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800293 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000294
joshualittabb52a12015-01-13 15:02:10 -0800295 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800296 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800297
egdaniel8dcdedc2015-11-11 06:27:20 -0800298 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800299 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800300 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700301 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000302
egdaniel8dcdedc2015-11-11 06:27:20 -0800303 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800304 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800305 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700306 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800307
cdalton85285412016-02-18 12:37:07 -0800308 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700309 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700310 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800311
joshualittabb52a12015-01-13 15:02:10 -0800312 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700313 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800314
315 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800316 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800317 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800318 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800319 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700320 egp.fInPosition->fName,
321 egp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800322 args.fTransformsIn,
323 args.fTransformsOut);
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) {
egdaniel4ca2e602015-11-18 08:01:26 -0800339 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
340 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
341 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
342 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
343 ellipseRadii.fsIn());
344 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
345 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 }
347
egdaniel4ca2e602015-11-18 08:01:26 -0800348 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000349 }
350
robertphillips46d36f02015-01-18 08:14:14 -0800351 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700352 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700353 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800354 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
355 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700356 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700357 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000358 }
359
egdaniel018fb622015-10-28 07:26:40 -0700360 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000361 }
362
joshualitte3ababe2015-05-15 07:56:07 -0700363 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700364 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700365 int index,
366 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomone4f24612016-08-17 10:30:17 -0700367 this->setTransformDataHelper(primProc.cast<EllipseGeometryProcessor>().fLocalMatrix,
368 pdman, index, transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700369 }
370
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000371 private:
egdaniele659a582015-11-13 09:55:43 -0800372 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000373 };
374
joshualitt71c92602015-01-14 08:12:47 -0800375 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800376 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800377 const Attribute* fInEllipseOffset;
378 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700379 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380 bool fStroke;
381
joshualittb0a8a372014-09-23 09:50:21 -0700382 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000383
joshualitt249af152014-09-15 11:41:13 -0700384 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000385};
386
bsalomoncdaa97b2016-03-08 08:30:14 -0800387GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000388
bungeman06ca8ec2016-06-09 08:01:03 -0700389sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
390 return sk_sp<GrGeometryProcessor>(
391 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392}
393
394///////////////////////////////////////////////////////////////////////////////
395
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000396/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000397 * 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 +0000398 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
399 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
400 * using differentials.
401 *
402 * The result is device-independent and can be used with any affine matrix.
403 */
404
bsalomoncdaa97b2016-03-08 08:30:14 -0800405enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000406
bsalomoncdaa97b2016-03-08 08:30:14 -0800407class DIEllipseGeometryProcessor : public GrGeometryProcessor {
408public:
409 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
410 : fViewMatrix(viewMatrix) {
411 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700412 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
413 kHigh_GrSLPrecision);
414 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
415 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
416 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800417 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000418 }
419
bsalomoncdaa97b2016-03-08 08:30:14 -0800420
421 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000422
mtklein36352bf2015-03-25 18:17:31 -0700423 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424
bsalomon31df31c2016-08-17 09:00:24 -0700425 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
426 GLSLProcessor::GenKey(*this, caps, b);
427 }
halcanary9d524f22016-03-29 09:03:52 -0700428
bsalomon31df31c2016-08-17 09:00:24 -0700429 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
430 return new GLSLProcessor();
431 }
432
433private:
egdaniel57d3b032015-11-13 11:57:27 -0800434 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000435 public:
egdaniel57d3b032015-11-13 11:57:27 -0800436 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800437 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000438
joshualitt465283c2015-09-11 08:19:35 -0700439 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800440 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800441 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800442 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800443 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444
joshualittabb52a12015-01-13 15:02:10 -0800445 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800446 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800447
egdaniel8dcdedc2015-11-11 06:27:20 -0800448 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800449 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800450 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700451 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700452
egdaniel8dcdedc2015-11-11 06:27:20 -0800453 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800454 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800455 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700456 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800457
cdalton85285412016-02-18 12:37:07 -0800458 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700459 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800460
joshualittabb52a12015-01-13 15:02:10 -0800461 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800462 this->setupPosition(vertBuilder,
463 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800464 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700465 diegp.fInPosition->fName,
466 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700467 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800468
469 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800470 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800471 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800472 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800473 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700474 diegp.fInPosition->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800475 args.fTransformsIn,
476 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800477
egdaniel4ca2e602015-11-18 08:01:26 -0800478 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800479 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000480 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800481 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
482 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
483 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
484 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
485 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
486 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800487 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
488 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000489
egdaniel4ca2e602015-11-18 08:01:26 -0800490 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000491 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
493 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800494 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000495 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800496 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
497 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000498 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800499 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 }
501
502 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800503 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
505 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
506 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
507 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
508 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
509 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
510 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
511 offsets1.fsIn());
512 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
513 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 }
515
egdaniel4ca2e602015-11-18 08:01:26 -0800516 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 }
518
robertphillips46d36f02015-01-18 08:14:14 -0800519 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700520 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700521 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800522 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
523 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700524 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700525 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000526 }
527
egdaniel018fb622015-10-28 07:26:40 -0700528 void setData(const GrGLSLProgramDataManager& pdman,
529 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800530 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700531
bsalomon31df31c2016-08-17 09:00:24 -0700532 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
533 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700534 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800535 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700536 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
537 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000538 }
539
540 private:
joshualitt5559ca22015-05-21 15:50:36 -0700541 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700542 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800543
egdaniele659a582015-11-13 09:55:43 -0800544 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000545 };
546
joshualitt71c92602015-01-14 08:12:47 -0800547 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800548 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800549 const Attribute* fInEllipseOffsets0;
550 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800551 SkMatrix fViewMatrix;
552 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000553
joshualittb0a8a372014-09-23 09:50:21 -0700554 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000555
joshualitt249af152014-09-15 11:41:13 -0700556 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000557};
558
bsalomoncdaa97b2016-03-08 08:30:14 -0800559GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560
bungeman06ca8ec2016-06-09 08:01:03 -0700561sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
562 return sk_sp<GrGeometryProcessor>(
563 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
564 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000565}
566
567///////////////////////////////////////////////////////////////////////////////
568
bsalomonabd30f52015-08-13 13:34:48 -0700569class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800570public:
reed1b55a962015-09-17 20:16:13 -0700571 DEFINE_BATCH_CLASS_ID
572
bsalomon4f3a0ca2016-08-22 13:14:26 -0700573 /** Optional extra params to render a partial arc rather than a full circle. */
574 struct ArcParams {
575 SkScalar fStartAngleRadians;
576 SkScalar fSweepAngleRadians;
577 bool fUseCenter;
578 };
579 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
580 SkScalar radius, const GrStyle& style,
581 const ArcParams* arcParams = nullptr) {
582 SkASSERT(circle_stays_circle(viewMatrix));
583 const SkStrokeRec& stroke = style.strokeRec();
584 if (style.hasPathEffect()) {
585 return nullptr;
586 }
587 SkStrokeRec::Style recStyle = stroke.getStyle();
588 if (arcParams) {
589 // Arc support depends on the style.
590 switch (recStyle) {
591 case SkStrokeRec::kStrokeAndFill_Style:
592 // This produces a strange result that this batch doesn't implement.
593 return nullptr;
594 case SkStrokeRec::kFill_Style:
595 // This supports all fills.
596 break;
597 case SkStrokeRec::kStroke_Style: // fall through
598 case SkStrokeRec::kHairline_Style:
599 // Strokes that don't use the center point are supported with butt cap.
600 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
601 return nullptr;
602 }
603 break;
604 }
605 }
606
bsalomon4b4a7cc2016-07-08 04:42:54 -0700607 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700608 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700609 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800610
bsalomon4f3a0ca2016-08-22 13:14:26 -0700611 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
612 SkStrokeRec::kHairline_Style == recStyle;
613 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700614
615 SkScalar innerRadius = 0.0f;
616 SkScalar outerRadius = radius;
617 SkScalar halfWidth = 0;
618 if (hasStroke) {
619 if (SkScalarNearlyZero(strokeWidth)) {
620 halfWidth = SK_ScalarHalf;
621 } else {
622 halfWidth = SkScalarHalf(strokeWidth);
623 }
624
625 outerRadius += halfWidth;
626 if (isStrokeOnly) {
627 innerRadius = radius - halfWidth;
628 }
629 }
630
631 // The radii are outset for two reasons. First, it allows the shader to simply perform
632 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
633 // Second, the outer radius is used to compute the verts of the bounding box that is
634 // rendered and the outset ensures the box will cover all partially covered by the circle.
635 outerRadius += SK_ScalarHalf;
636 innerRadius -= SK_ScalarHalf;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700637 CircleBatch* batch = new CircleBatch();
638 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639
bsalomon4f3a0ca2016-08-22 13:14:26 -0700640 // This makes every point fully inside the intersection plane.
641 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
642 // This makes every point fully outside the union plane.
643 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
644 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
645 center.fX + outerRadius, center.fY + outerRadius);
646
647 if (arcParams) {
648 // The shader operates in a space where the circle is translated to be centered at the
649 // origin. Here we compute points on the unit circle at the starting and ending angles.
650 SkPoint startPoint, stopPoint;
651 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
652 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
653 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
654 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
655 // radial lines. However, in both cases we have to be careful about the half-circle.
656 // case. In that case the two radial lines are equal and so that edge gets clipped
657 // twice. Since the shared edge goes through the center we fall back on the useCenter
658 // case.
659 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
660 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
661 SK_ScalarPI);
662 if (useCenter) {
663 SkVector norm0 = {startPoint.fY, -startPoint.fX};
664 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
665 if (arcParams->fSweepAngleRadians > 0) {
666 norm0.negate();
667 } else {
668 norm1.negate();
669 }
670 batch->fClipPlane = true;
671 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
672 batch->fGeoData.emplace_back(Geometry {
673 color,
674 innerRadius,
675 outerRadius,
676 {norm0.fX, norm0.fY, 0.5f},
677 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
678 {norm1.fX, norm1.fY, 0.5f},
679 devBounds
680 });
681 batch->fClipPlaneIsect = false;
682 batch->fClipPlaneUnion = true;
683 } else {
684 batch->fGeoData.emplace_back(Geometry {
685 color,
686 innerRadius,
687 outerRadius,
688 {norm0.fX, norm0.fY, 0.5f},
689 {norm1.fX, norm1.fY, 0.5f},
690 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
691 devBounds
692 });
693 batch->fClipPlaneIsect = true;
694 batch->fClipPlaneUnion = false;
695 }
696 } else {
697 // We clip to a secant of the original circle.
698 startPoint.scale(radius);
699 stopPoint.scale(radius);
700 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
701 norm.normalize();
702 if (arcParams->fSweepAngleRadians > 0) {
703 norm.negate();
704 }
705 SkScalar d = -norm.dot(startPoint) + 0.5f;
706
707 batch->fGeoData.emplace_back(Geometry {
708 color,
709 innerRadius,
710 outerRadius,
711 {norm.fX, norm.fY, d},
712 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
713 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
714 devBounds
715 });
716 batch->fClipPlane = true;
717 batch->fClipPlaneIsect = false;
718 batch->fClipPlaneUnion = false;
719 }
720 } else {
721 batch->fGeoData.emplace_back(Geometry {
722 color,
723 innerRadius,
724 outerRadius,
725 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
726 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
727 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
728 devBounds
729 });
730 batch->fClipPlane = false;
731 batch->fClipPlaneIsect = false;
732 batch->fClipPlaneUnion = false;
733 }
bsalomon88cf17d2016-07-08 06:40:56 -0700734 // Use the original radius and stroke radius for the bounds so that it does not include the
735 // AA bloat.
736 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700737 batch->setBounds({center.fX - radius, center.fY - radius,
738 center.fX + radius, center.fY + radius},
739 HasAABloat::kYes, IsZeroArea::kNo);
740 batch->fStroked = isStrokeOnly && innerRadius > 0;
741 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800742 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700743
mtklein36352bf2015-03-25 18:17:31 -0700744 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800745
robertphillipse004bfc2015-11-16 09:06:59 -0800746 SkString dumpInfo() const override {
747 SkString string;
748 for (int i = 0; i < fGeoData.count(); ++i) {
749 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
750 "InnerRad: %.2f, OuterRad: %.2f\n",
751 fGeoData[i].fColor,
752 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
753 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
754 fGeoData[i].fInnerRadius,
755 fGeoData[i].fOuterRadius);
756 }
757 string.append(INHERITED::dumpInfo());
758 return string;
759 }
760
halcanary9d524f22016-03-29 09:03:52 -0700761 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800762 GrInitInvariantOutput* coverage,
763 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800764 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800765 color->setKnownFourComponents(fGeoData[0].fColor);
766 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800767 }
768
bsalomone46f9fe2015-08-18 06:05:14 -0700769private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700770 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800771 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800772 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800773 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800774 if (!overrides.readsLocalCoords()) {
775 fViewMatrixIfUsingLocalCoords.reset();
776 }
joshualitt76e7fb62015-02-11 08:52:27 -0800777 }
778
joshualitt144c3c82015-11-30 12:30:13 -0800779 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800780 SkMatrix localMatrix;
781 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800782 return;
783 }
784
785 // Setup geometry processor
bsalomon4f3a0ca2016-08-22 13:14:26 -0700786 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, fClipPlane,
787 fClipPlaneIsect,
788 fClipPlaneUnion,
789 localMatrix));
790
791 struct CircleVertex {
792 SkPoint fPos;
793 GrColor fColor;
794 SkPoint fOffset;
795 SkScalar fOuterRadius;
796 SkScalar fInnerRadius;
797 // These planes may or may not be present in the vertex buffer.
798 SkScalar fHalfPlanes[3][3];
799 };
joshualitt76e7fb62015-02-11 08:52:27 -0800800
joshualitt76e7fb62015-02-11 08:52:27 -0800801 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800802 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700803 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
804 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
805 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
bsalomonb5238a72015-05-05 07:49:49 -0700806 QuadHelper helper;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700807 char* vertices = reinterpret_cast<char*>(helper.init(target, vertexStride, instanceCount));
808 if (!vertices) {
joshualitt4b31de82015-03-05 14:33:41 -0800809 return;
810 }
811
joshualitt76e7fb62015-02-11 08:52:27 -0800812 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800813 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800814
brianosmanbb2ff942016-02-11 14:15:18 -0800815 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700816 SkScalar innerRadius = geom.fInnerRadius;
817 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800818
bsalomonb5238a72015-05-05 07:49:49 -0700819 const SkRect& bounds = geom.fDevBounds;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700820 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 0)*vertexStride);
821 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 1)*vertexStride);
822 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 2)*vertexStride);
823 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 3)*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800824
825 // The inner radius in the vertex data must be specified in normalized space.
826 innerRadius = innerRadius / outerRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700827 v0->fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
828 v0->fColor = color;
829 v0->fOffset = SkPoint::Make(-1, -1);
830 v0->fOuterRadius = outerRadius;
831 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800832
bsalomon4f3a0ca2016-08-22 13:14:26 -0700833 v1->fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
834 v1->fColor = color;
835 v1->fOffset = SkPoint::Make(-1, 1);
836 v1->fOuterRadius = outerRadius;
837 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800838
bsalomon4f3a0ca2016-08-22 13:14:26 -0700839 v2->fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
840 v2->fColor = color;
841 v2->fOffset = SkPoint::Make(1, 1);
842 v2->fOuterRadius = outerRadius;
843 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800844
bsalomon4f3a0ca2016-08-22 13:14:26 -0700845 v3->fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
846 v3->fColor = color;
847 v3->fOffset = SkPoint::Make(1, -1);
848 v3->fOuterRadius = outerRadius;
849 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800850
bsalomon4f3a0ca2016-08-22 13:14:26 -0700851 if (fClipPlane) {
852 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
853 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
854 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
855 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
856 }
857 int unionIdx = 1;
858 if (fClipPlaneIsect) {
859 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
860 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
861 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
862 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
863 unionIdx = 2;
864 }
865 if (fClipPlaneUnion) {
866 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
867 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
868 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
869 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
870 }
joshualitt76e7fb62015-02-11 08:52:27 -0800871 }
bsalomon342bfc22016-04-01 06:06:20 -0700872 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800873 }
874
bsalomoncb02b382015-08-12 11:14:50 -0700875 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700876 CircleBatch* that = t->cast<CircleBatch>();
877 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
878 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700879 return false;
880 }
881
bsalomoncdaa97b2016-03-08 08:30:14 -0800882 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800883 return false;
884 }
885
bsalomon4f3a0ca2016-08-22 13:14:26 -0700886 // Because we've set up the batches that don't use the planes with noop values
887 // we can just accumulate used planes by later batches.
888 fClipPlane |= that->fClipPlane;
889 fClipPlaneIsect |= that->fClipPlaneIsect;
890 fClipPlaneUnion |= that->fClipPlaneUnion;
891
bsalomoncdaa97b2016-03-08 08:30:14 -0800892 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800893 return false;
894 }
895
bsalomoncdaa97b2016-03-08 08:30:14 -0800896 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700897 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800898 return true;
899 }
900
bsalomon4b4a7cc2016-07-08 04:42:54 -0700901 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700902 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700903 SkScalar fInnerRadius;
904 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700905 SkScalar fClipPlane[3];
906 SkScalar fIsectPlane[3];
907 SkScalar fUnionPlane[3];
908 SkRect fDevBounds;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700909 };
910
bsalomoncdaa97b2016-03-08 08:30:14 -0800911 bool fStroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700912 bool fClipPlane;
913 bool fClipPlaneIsect;
914 bool fClipPlaneUnion;
bsalomoncdaa97b2016-03-08 08:30:14 -0800915 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800916 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700917
918 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800919};
920
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000921///////////////////////////////////////////////////////////////////////////////
922
bsalomonabd30f52015-08-13 13:34:48 -0700923class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800924public:
reed1b55a962015-09-17 20:16:13 -0700925 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -0700926 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
927 const SkStrokeRec& stroke) {
928 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -0700929
bsalomon4b4a7cc2016-07-08 04:42:54 -0700930 // do any matrix crunching before we reset the draw state for device coords
931 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
932 viewMatrix.mapPoints(&center, 1);
933 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
934 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
935 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
936 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
937 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
938 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800939
bsalomon4b4a7cc2016-07-08 04:42:54 -0700940 // do (potentially) anisotropic mapping of stroke
941 SkVector scaledStroke;
942 SkScalar strokeWidth = stroke.getWidth();
943 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
944 viewMatrix[SkMatrix::kMSkewY]));
945 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
946 viewMatrix[SkMatrix::kMScaleY]));
947
948 SkStrokeRec::Style style = stroke.getStyle();
949 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
950 SkStrokeRec::kHairline_Style == style;
951 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
952
953 SkScalar innerXRadius = 0;
954 SkScalar innerYRadius = 0;
955 if (hasStroke) {
956 if (SkScalarNearlyZero(scaledStroke.length())) {
957 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
958 } else {
959 scaledStroke.scale(SK_ScalarHalf);
960 }
961
962 // we only handle thick strokes for near-circular ellipses
963 if (scaledStroke.length() > SK_ScalarHalf &&
964 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
965 return nullptr;
966 }
967
968 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
969 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
970 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
971 return nullptr;
972 }
973
974 // this is legit only if scale & translation (which should be the case at the moment)
975 if (isStrokeOnly) {
976 innerXRadius = xRadius - scaledStroke.fX;
977 innerYRadius = yRadius - scaledStroke.fY;
978 }
979
980 xRadius += scaledStroke.fX;
981 yRadius += scaledStroke.fY;
982 }
983
984 EllipseBatch* batch = new EllipseBatch();
985 batch->fGeoData.emplace_back(Geometry {
986 color,
987 xRadius,
988 yRadius,
989 innerXRadius,
990 innerYRadius,
991 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
992 center.fX + xRadius, center.fY + yRadius)
993 });
994
bsalomon88cf17d2016-07-08 06:40:56 -0700995 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
996
bsalomon4b4a7cc2016-07-08 04:42:54 -0700997 // Outset bounds to include half-pixel width antialiasing.
998 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
999
1000 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1001 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001002 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -08001003 }
joshualitt76e7fb62015-02-11 08:52:27 -08001004
mtklein36352bf2015-03-25 18:17:31 -07001005 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001006
halcanary9d524f22016-03-29 09:03:52 -07001007 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001008 GrInitInvariantOutput* coverage,
1009 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001010 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001011 color->setKnownFourComponents(fGeoData[0].fColor);
1012 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001013 }
1014
bsalomone46f9fe2015-08-18 06:05:14 -07001015private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001016 EllipseBatch() : INHERITED(ClassID()) {}
1017
ethannicholasff210322015-11-24 12:10:10 -08001018 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001019 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001020 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001021 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001022 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001023 if (!overrides.readsLocalCoords()) {
1024 fViewMatrixIfUsingLocalCoords.reset();
1025 }
joshualitt76e7fb62015-02-11 08:52:27 -08001026 }
1027
joshualitt144c3c82015-11-30 12:30:13 -08001028 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001029 SkMatrix localMatrix;
1030 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001031 return;
1032 }
1033
1034 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001035 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001036
joshualitt76e7fb62015-02-11 08:52:27 -08001037 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001038 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001039 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001040 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001041 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001042 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001043 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001044 return;
1045 }
1046
bsalomon8415abe2015-05-04 11:41:41 -07001047 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001048 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001049
brianosmanbb2ff942016-02-11 14:15:18 -08001050 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001051 SkScalar xRadius = geom.fXRadius;
1052 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001053
1054 // Compute the reciprocals of the radii here to save time in the shader
1055 SkScalar xRadRecip = SkScalarInvert(xRadius);
1056 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001057 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1058 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001059
bsalomonb5238a72015-05-05 07:49:49 -07001060 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001061
vjiaoblack977996d2016-06-30 12:20:54 -07001062 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1063 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1064 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1065
joshualitt76e7fb62015-02-11 08:52:27 -08001066 // The inner radius in the vertex data must be specified in normalized space.
1067 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001068 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001069 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001070 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1071 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1072
1073 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001074 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001075 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001076 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1077 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1078
1079 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001080 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001081 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001082 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1083 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1084
1085 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001086 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001087 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001088 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1089 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1090
bsalomonb5238a72015-05-05 07:49:49 -07001091 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001092 }
bsalomon342bfc22016-04-01 06:06:20 -07001093 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001094 }
1095
bsalomoncb02b382015-08-12 11:14:50 -07001096 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001097 EllipseBatch* that = t->cast<EllipseBatch>();
1098
1099 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1100 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001101 return false;
1102 }
1103
bsalomoncdaa97b2016-03-08 08:30:14 -08001104 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001105 return false;
1106 }
1107
bsalomoncdaa97b2016-03-08 08:30:14 -08001108 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001109 return false;
1110 }
1111
bsalomoncdaa97b2016-03-08 08:30:14 -08001112 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001113 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001114 return true;
1115 }
1116
bsalomon4b4a7cc2016-07-08 04:42:54 -07001117 struct Geometry {
1118 GrColor fColor;
1119 SkScalar fXRadius;
1120 SkScalar fYRadius;
1121 SkScalar fInnerXRadius;
1122 SkScalar fInnerYRadius;
1123 SkRect fDevBounds;
1124 };
joshualitt76e7fb62015-02-11 08:52:27 -08001125
bsalomoncdaa97b2016-03-08 08:30:14 -08001126 bool fStroked;
1127 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001128 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001129
1130 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001131};
1132
joshualitt76e7fb62015-02-11 08:52:27 -08001133/////////////////////////////////////////////////////////////////////////////////////////////////
1134
bsalomonabd30f52015-08-13 13:34:48 -07001135class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001136public:
reed1b55a962015-09-17 20:16:13 -07001137 DEFINE_BATCH_CLASS_ID
1138
bsalomon4b4a7cc2016-07-08 04:42:54 -07001139 static GrDrawBatch* Create(GrColor color,
1140 const SkMatrix& viewMatrix,
1141 const SkRect& ellipse,
1142 const SkStrokeRec& stroke) {
1143 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1144 SkScalar xRadius = SkScalarHalf(ellipse.width());
1145 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001146
bsalomon4b4a7cc2016-07-08 04:42:54 -07001147 SkStrokeRec::Style style = stroke.getStyle();
1148 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1149 DIEllipseStyle::kStroke :
1150 (SkStrokeRec::kHairline_Style == style) ?
1151 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1152
1153 SkScalar innerXRadius = 0;
1154 SkScalar innerYRadius = 0;
1155 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1156 SkScalar strokeWidth = stroke.getWidth();
1157
1158 if (SkScalarNearlyZero(strokeWidth)) {
1159 strokeWidth = SK_ScalarHalf;
1160 } else {
1161 strokeWidth *= SK_ScalarHalf;
1162 }
1163
1164 // we only handle thick strokes for near-circular ellipses
1165 if (strokeWidth > SK_ScalarHalf &&
1166 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1167 return nullptr;
1168 }
1169
1170 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1171 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1172 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1173 return nullptr;
1174 }
1175
1176 // set inner radius (if needed)
1177 if (SkStrokeRec::kStroke_Style == style) {
1178 innerXRadius = xRadius - strokeWidth;
1179 innerYRadius = yRadius - strokeWidth;
1180 }
1181
1182 xRadius += strokeWidth;
1183 yRadius += strokeWidth;
1184 }
1185 if (DIEllipseStyle::kStroke == dieStyle) {
1186 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1187 DIEllipseStyle ::kFill;
1188 }
1189
1190 // This expands the outer rect so that after CTM we end up with a half-pixel border
1191 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1192 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1193 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1194 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1195 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1196 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1197
1198 DIEllipseBatch* batch = new DIEllipseBatch();
1199 batch->fGeoData.emplace_back(Geometry {
1200 viewMatrix,
1201 color,
1202 xRadius,
1203 yRadius,
1204 innerXRadius,
1205 innerYRadius,
1206 geoDx,
1207 geoDy,
1208 dieStyle,
1209 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1210 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1211 });
bsalomon88cf17d2016-07-08 06:40:56 -07001212 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1213 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001214 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001215 }
1216
mtklein36352bf2015-03-25 18:17:31 -07001217 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001218
halcanary9d524f22016-03-29 09:03:52 -07001219 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001220 GrInitInvariantOutput* coverage,
1221 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001222 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001223 color->setKnownFourComponents(fGeoData[0].fColor);
1224 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001225 }
1226
bsalomone46f9fe2015-08-18 06:05:14 -07001227private:
1228
bsalomon4b4a7cc2016-07-08 04:42:54 -07001229 DIEllipseBatch() : INHERITED(ClassID()) {}
1230
ethannicholasff210322015-11-24 12:10:10 -08001231 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001232 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001233 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001234 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001235 }
1236
joshualitt144c3c82015-11-30 12:30:13 -08001237 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001238 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001239 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1240 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001241
joshualitt76e7fb62015-02-11 08:52:27 -08001242 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001243 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001244 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001245 QuadHelper helper;
1246 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001247 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001248 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001249 return;
1250 }
1251
joshualitt76e7fb62015-02-11 08:52:27 -08001252 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001253 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001254
brianosmanbb2ff942016-02-11 14:15:18 -08001255 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001256 SkScalar xRadius = geom.fXRadius;
1257 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001258
bsalomonb5238a72015-05-05 07:49:49 -07001259 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001260
1261 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001262 SkScalar offsetDx = geom.fGeoDx / xRadius;
1263 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001264
reed80ea19c2015-05-12 10:37:34 -07001265 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1266 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001267
1268 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001269 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001270 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1271 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1272
1273 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001274 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001275 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1276 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1277
1278 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001279 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001280 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1281 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1282
1283 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001284 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001285 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1286 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1287
bsalomonb5238a72015-05-05 07:49:49 -07001288 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001289 }
bsalomon342bfc22016-04-01 06:06:20 -07001290 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001291 }
halcanary9d524f22016-03-29 09:03:52 -07001292
bsalomoncb02b382015-08-12 11:14:50 -07001293 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001294 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1295 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1296 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001297 return false;
1298 }
1299
bsalomoncdaa97b2016-03-08 08:30:14 -08001300 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001301 return false;
1302 }
1303
joshualittd96a67b2015-05-05 14:09:05 -07001304 // TODO rewrite to allow positioning on CPU
1305 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001306 return false;
1307 }
1308
bsalomoncdaa97b2016-03-08 08:30:14 -08001309 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001310 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001311 return true;
1312 }
1313
joshualitt76e7fb62015-02-11 08:52:27 -08001314 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001315 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001316
bsalomon4b4a7cc2016-07-08 04:42:54 -07001317 struct Geometry {
1318 SkMatrix fViewMatrix;
1319 GrColor fColor;
1320 SkScalar fXRadius;
1321 SkScalar fYRadius;
1322 SkScalar fInnerXRadius;
1323 SkScalar fInnerYRadius;
1324 SkScalar fGeoDx;
1325 SkScalar fGeoDy;
1326 DIEllipseStyle fStyle;
1327 SkRect fBounds;
1328 };
1329
bsalomoncdaa97b2016-03-08 08:30:14 -08001330 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001331 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001332
1333 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001334};
1335
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001336///////////////////////////////////////////////////////////////////////////////
1337
1338static const uint16_t gRRectIndices[] = {
1339 // corners
1340 0, 1, 5, 0, 5, 4,
1341 2, 3, 7, 2, 7, 6,
1342 8, 9, 13, 8, 13, 12,
1343 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001344
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001345 // edges
1346 1, 2, 6, 1, 6, 5,
1347 4, 5, 9, 4, 9, 8,
1348 6, 7, 11, 6, 11, 10,
1349 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001350
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001351 // center
1352 // we place this at the end so that we can ignore these indices when rendering stroke-only
1353 5, 6, 10, 5, 10, 9
1354};
1355
joshualitt5ead6da2014-10-22 16:00:29 -07001356static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1357static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1358static const int kVertsPerRRect = 16;
1359static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001360
bsalomoned0bcad2015-05-04 10:36:42 -07001361GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1362GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
cdalton397536c2016-03-25 12:15:03 -07001363static const GrBuffer* ref_rrect_index_buffer(bool strokeOnly,
1364 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001365 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1366 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1367 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001368 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001369 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1370 gStrokeRRectOnlyIndexBufferKey);
1371 } else {
bsalomoneae62002015-07-31 13:59:30 -07001372 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001373 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1374 gRRectOnlyIndexBufferKey);
1375
1376 }
1377}
1378
joshualitt76e7fb62015-02-11 08:52:27 -08001379///////////////////////////////////////////////////////////////////////////////////////////////////
1380
bsalomonabd30f52015-08-13 13:34:48 -07001381class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001382public:
reed1b55a962015-09-17 20:16:13 -07001383 DEFINE_BATCH_CLASS_ID
1384
bsalomon4b4a7cc2016-07-08 04:42:54 -07001385 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1386 // whether the rrect is only stroked or stroked and filled.
1387 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1388 float devRadius, float devStrokeWidth, bool strokeOnly)
1389 : INHERITED(ClassID())
1390 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1391 SkRect bounds = devRect;
1392 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1393 SkScalar innerRadius = 0.0f;
1394 SkScalar outerRadius = devRadius;
1395 SkScalar halfWidth = 0;
1396 fStroked = false;
1397 if (devStrokeWidth > 0) {
1398 if (SkScalarNearlyZero(devStrokeWidth)) {
1399 halfWidth = SK_ScalarHalf;
1400 } else {
1401 halfWidth = SkScalarHalf(devStrokeWidth);
1402 }
joshualitt76e7fb62015-02-11 08:52:27 -08001403
bsalomon4b4a7cc2016-07-08 04:42:54 -07001404 if (strokeOnly) {
1405 innerRadius = devRadius - halfWidth;
1406 fStroked = innerRadius >= 0;
1407 }
1408 outerRadius += halfWidth;
1409 bounds.outset(halfWidth, halfWidth);
1410 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001411
bsalomon4b4a7cc2016-07-08 04:42:54 -07001412 // The radii are outset for two reasons. First, it allows the shader to simply perform
1413 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1414 // Second, the outer radius is used to compute the verts of the bounding box that is
1415 // rendered and the outset ensures the box will cover all partially covered by the rrect
1416 // corners.
1417 outerRadius += SK_ScalarHalf;
1418 innerRadius -= SK_ScalarHalf;
1419
bsalomon88cf17d2016-07-08 06:40:56 -07001420 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1421
1422 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001423 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1424
1425 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds });
joshualitt76e7fb62015-02-11 08:52:27 -08001426 }
1427
mtklein36352bf2015-03-25 18:17:31 -07001428 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001429
halcanary9d524f22016-03-29 09:03:52 -07001430 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001431 GrInitInvariantOutput* coverage,
1432 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001433 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001434 color->setKnownFourComponents(fGeoData[0].fColor);
1435 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001436 }
1437
bsalomone46f9fe2015-08-18 06:05:14 -07001438private:
ethannicholasff210322015-11-24 12:10:10 -08001439 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001440 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001441 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001442 if (!overrides.readsLocalCoords()) {
1443 fViewMatrixIfUsingLocalCoords.reset();
1444 }
joshualitt76e7fb62015-02-11 08:52:27 -08001445 }
1446
joshualitt144c3c82015-11-30 12:30:13 -08001447 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001448 // Invert the view matrix as a local matrix (if any other processors require coords).
1449 SkMatrix localMatrix;
1450 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001451 return;
1452 }
1453
1454 // Setup geometry processor
bsalomon4f3a0ca2016-08-22 13:14:26 -07001455 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, false, false,
1456 false, localMatrix));
1457
1458 struct CircleVertex {
1459 SkPoint fPos;
1460 GrColor fColor;
1461 SkPoint fOffset;
1462 SkScalar fOuterRadius;
1463 SkScalar fInnerRadius;
1464 // No half plane, we don't use it here.
1465 };
joshualitt76e7fb62015-02-11 08:52:27 -08001466
joshualitt76e7fb62015-02-11 08:52:27 -08001467 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001468 size_t vertexStride = gp->getVertexStride();
1469 SkASSERT(vertexStride == sizeof(CircleVertex));
1470
bsalomonb5238a72015-05-05 07:49:49 -07001471 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001472 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001473 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001474 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001475
bsalomonb5238a72015-05-05 07:49:49 -07001476 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001477 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001478 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1479 indicesPerInstance, instanceCount));
1480 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001481 SkDebugf("Could not allocate vertices\n");
1482 return;
1483 }
1484
joshualitt76e7fb62015-02-11 08:52:27 -08001485 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001486 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001487
brianosmanbb2ff942016-02-11 14:15:18 -08001488 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001489 SkScalar outerRadius = args.fOuterRadius;
1490
egdanielbc227142015-04-21 06:28:08 -07001491 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001492
1493 SkScalar yCoords[4] = {
1494 bounds.fTop,
1495 bounds.fTop + outerRadius,
1496 bounds.fBottom - outerRadius,
1497 bounds.fBottom
1498 };
1499
1500 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1501 // The inner radius in the vertex data must be specified in normalized space.
1502 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1503 for (int i = 0; i < 4; ++i) {
1504 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001505 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001506 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1507 verts->fOuterRadius = outerRadius;
1508 verts->fInnerRadius = innerRadius;
1509 verts++;
1510
1511 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001512 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001513 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1514 verts->fOuterRadius = outerRadius;
1515 verts->fInnerRadius = innerRadius;
1516 verts++;
1517
1518 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001519 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001520 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1521 verts->fOuterRadius = outerRadius;
1522 verts->fInnerRadius = innerRadius;
1523 verts++;
1524
1525 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001526 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001527 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1528 verts->fOuterRadius = outerRadius;
1529 verts->fInnerRadius = innerRadius;
1530 verts++;
1531 }
1532 }
1533
bsalomon342bfc22016-04-01 06:06:20 -07001534 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001535 }
1536
bsalomoncb02b382015-08-12 11:14:50 -07001537 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001538 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1539 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1540 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001541 return false;
1542 }
1543
bsalomoncdaa97b2016-03-08 08:30:14 -08001544 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001545 return false;
1546 }
1547
bsalomoncdaa97b2016-03-08 08:30:14 -08001548 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001549 return false;
1550 }
1551
bsalomoncdaa97b2016-03-08 08:30:14 -08001552 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001553 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001554 return true;
1555 }
1556
bsalomon4b4a7cc2016-07-08 04:42:54 -07001557 struct Geometry {
1558 GrColor fColor;
1559 SkScalar fInnerRadius;
1560 SkScalar fOuterRadius;
1561 SkRect fDevBounds;
1562 };
1563
bsalomoncdaa97b2016-03-08 08:30:14 -08001564 bool fStroked;
1565 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001566 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001567
1568 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001569};
1570
bsalomonabd30f52015-08-13 13:34:48 -07001571class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001572public:
reed1b55a962015-09-17 20:16:13 -07001573 DEFINE_BATCH_CLASS_ID
1574
bsalomon4b4a7cc2016-07-08 04:42:54 -07001575 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1576 // whether the rrect is only stroked or stroked and filled.
1577 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1578 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1579 bool strokeOnly) {
1580 SkASSERT(devXRadius > 0.5);
1581 SkASSERT(devYRadius > 0.5);
1582 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1583 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1584 SkScalar innerXRadius = 0.0f;
1585 SkScalar innerYRadius = 0.0f;
1586 SkRect bounds = devRect;
1587 bool stroked = false;
1588 if (devStrokeWidths.fX > 0) {
1589 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1590 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1591 } else {
1592 devStrokeWidths.scale(SK_ScalarHalf);
1593 }
joshualitt76e7fb62015-02-11 08:52:27 -08001594
bsalomon4b4a7cc2016-07-08 04:42:54 -07001595 // we only handle thick strokes for near-circular ellipses
1596 if (devStrokeWidths.length() > SK_ScalarHalf &&
1597 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1598 return nullptr;
1599 }
1600
1601 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1602 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1603 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1604 return nullptr;
1605 }
1606 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1607 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1608 return nullptr;
1609 }
1610
1611 // this is legit only if scale & translation (which should be the case at the moment)
1612 if (strokeOnly) {
1613 innerXRadius = devXRadius - devStrokeWidths.fX;
1614 innerYRadius = devYRadius - devStrokeWidths.fY;
1615 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1616 }
1617
1618 devXRadius += devStrokeWidths.fX;
1619 devYRadius += devStrokeWidths.fY;
1620 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1621 }
1622
bsalomon4b4a7cc2016-07-08 04:42:54 -07001623 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1624 batch->fStroked = stroked;
1625 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001626 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1627 // Expand the rect for aa in order to generate the correct vertices.
1628 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001629 batch->fGeoData.emplace_back(
1630 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001631 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001632 }
1633
mtklein36352bf2015-03-25 18:17:31 -07001634 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001635
halcanary9d524f22016-03-29 09:03:52 -07001636 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001637 GrInitInvariantOutput* coverage,
1638 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001639 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001640 color->setKnownFourComponents(fGeoData[0].fColor);
1641 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001642 }
1643
bsalomone46f9fe2015-08-18 06:05:14 -07001644private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001645 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1646
ethannicholasff210322015-11-24 12:10:10 -08001647 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001648 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001649 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001650 if (!overrides.readsLocalCoords()) {
1651 fViewMatrixIfUsingLocalCoords.reset();
1652 }
joshualitt76e7fb62015-02-11 08:52:27 -08001653 }
1654
joshualitt144c3c82015-11-30 12:30:13 -08001655 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001656 SkMatrix localMatrix;
1657 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001658 return;
1659 }
1660
1661 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001662 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001663
joshualitt76e7fb62015-02-11 08:52:27 -08001664 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001665 size_t vertexStride = gp->getVertexStride();
1666 SkASSERT(vertexStride == sizeof(EllipseVertex));
1667
bsalomonb5238a72015-05-05 07:49:49 -07001668 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001669 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001670 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001671 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001672
bsalomonb5238a72015-05-05 07:49:49 -07001673 InstancedHelper helper;
1674 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001675 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001676 kVertsPerRRect, indicesPerInstance, instanceCount));
1677 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001678 SkDebugf("Could not allocate vertices\n");
1679 return;
1680 }
1681
joshualitt76e7fb62015-02-11 08:52:27 -08001682 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001683 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001684
brianosmanbb2ff942016-02-11 14:15:18 -08001685 GrColor color = args.fColor;
1686
joshualitt76e7fb62015-02-11 08:52:27 -08001687 // Compute the reciprocals of the radii here to save time in the shader
1688 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1689 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1690 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1691 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1692
1693 // Extend the radii out half a pixel to antialias.
1694 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1695 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1696
egdanielbc227142015-04-21 06:28:08 -07001697 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001698
1699 SkScalar yCoords[4] = {
1700 bounds.fTop,
1701 bounds.fTop + yOuterRadius,
1702 bounds.fBottom - yOuterRadius,
1703 bounds.fBottom
1704 };
1705 SkScalar yOuterOffsets[4] = {
1706 yOuterRadius,
1707 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1708 SK_ScalarNearlyZero,
1709 yOuterRadius
1710 };
1711
1712 for (int i = 0; i < 4; ++i) {
1713 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001714 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001715 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1716 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1717 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1718 verts++;
1719
1720 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001721 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001722 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1723 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1724 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1725 verts++;
1726
1727 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001728 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001729 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1730 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1731 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1732 verts++;
1733
1734 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001735 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001736 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1737 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1738 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1739 verts++;
1740 }
1741 }
bsalomon342bfc22016-04-01 06:06:20 -07001742 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001743 }
1744
bsalomoncb02b382015-08-12 11:14:50 -07001745 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001746 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1747
1748 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1749 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001750 return false;
1751 }
1752
bsalomoncdaa97b2016-03-08 08:30:14 -08001753 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001754 return false;
1755 }
1756
bsalomoncdaa97b2016-03-08 08:30:14 -08001757 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001758 return false;
1759 }
1760
bsalomoncdaa97b2016-03-08 08:30:14 -08001761 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001762 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001763 return true;
1764 }
1765
bsalomon4b4a7cc2016-07-08 04:42:54 -07001766 struct Geometry {
1767 GrColor fColor;
1768 SkScalar fXRadius;
1769 SkScalar fYRadius;
1770 SkScalar fInnerXRadius;
1771 SkScalar fInnerYRadius;
1772 SkRect fDevBounds;
1773 };
1774
bsalomoncdaa97b2016-03-08 08:30:14 -08001775 bool fStroked;
1776 SkMatrix fViewMatrixIfUsingLocalCoords;
1777 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001778
1779 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001780};
1781
bsalomonabd30f52015-08-13 13:34:48 -07001782static GrDrawBatch* create_rrect_batch(GrColor color,
1783 const SkMatrix& viewMatrix,
1784 const SkRRect& rrect,
1785 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001786 SkASSERT(viewMatrix.rectStaysRect());
1787 SkASSERT(rrect.isSimple());
1788 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001789
joshualitt3e708c52015-04-30 13:49:27 -07001790 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001791 // do any matrix crunching before we reset the draw state for device coords
1792 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001793 SkRect bounds;
1794 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001795
1796 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001797 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1798 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1799 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1800 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001801
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001802 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001803
bsalomon4b4a7cc2016-07-08 04:42:54 -07001804 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1805 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001806 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001807
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001808 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1809 SkStrokeRec::kHairline_Style == style;
1810 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1811
1812 if (hasStroke) {
1813 if (SkStrokeRec::kHairline_Style == style) {
1814 scaledStroke.set(1, 1);
1815 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001816 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1817 viewMatrix[SkMatrix::kMSkewY]));
1818 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1819 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001820 }
1821
1822 // if half of strokewidth is greater than radius, we don't handle that right now
1823 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001824 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001825 }
1826 }
1827
1828 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1829 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1830 // patch will have fractional coverage. This only matters when the interior is actually filled.
1831 // We could consider falling back to rect rendering here, since a tiny radius is
1832 // indistinguishable from a square corner.
1833 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001834 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001835 }
1836
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001837 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001838 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001839 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
1840 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001841 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001842 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001843 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
1844 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001845
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001846 }
joshualitt3e708c52015-04-30 13:49:27 -07001847}
1848
robertphillipsb56f9272016-02-25 11:03:52 -08001849GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001850 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001851 const SkRRect& rrect,
1852 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001853 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001854 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001855 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001856 }
1857
1858 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001859 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001860 }
1861
robertphillips0cc2f852016-02-24 13:36:56 -08001862 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001863}
joshualitt3e708c52015-04-30 13:49:27 -07001864
bsalomon4b4a7cc2016-07-08 04:42:54 -07001865///////////////////////////////////////////////////////////////////////////////
1866
1867GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
1868 const SkMatrix& viewMatrix,
1869 const SkRect& oval,
1870 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001871 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001872 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07001873 SkScalar width = oval.width();
1874 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
1875 SkPoint center = {oval.centerX(), oval.centerY()};
1876 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
1877 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07001878 }
1879
1880 // if we have shader derivative support, render as device-independent
1881 if (shaderCaps->shaderDerivativeSupport()) {
1882 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
1883 }
1884
1885 // otherwise axis-aligned ellipses only
1886 if (viewMatrix.rectStaysRect()) {
1887 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
1888 }
1889
1890 return nullptr;
1891}
1892
1893///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07001894
bsalomon4f3a0ca2016-08-22 13:14:26 -07001895GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
1896 const SkMatrix& viewMatrix,
1897 const SkRect& oval,
1898 SkScalar startAngle, SkScalar sweepAngle,
1899 bool useCenter,
1900 const GrStyle& style,
1901 const GrShaderCaps* shaderCaps) {
1902 SkScalar width = oval.width();
1903 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
1904 return nullptr;
1905 }
1906 SkPoint center = {oval.centerX(), oval.centerY()};
1907 CircleBatch::ArcParams arcParams = {
1908 SkDegreesToRadians(startAngle),
1909 SkDegreesToRadians(sweepAngle),
1910 useCenter
1911 };
1912 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
1913}
1914
1915///////////////////////////////////////////////////////////////////////////////
1916
joshualitt3e708c52015-04-30 13:49:27 -07001917#ifdef GR_TEST_UTILS
1918
bsalomonabd30f52015-08-13 13:34:48 -07001919DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001920 do {
bsalomoncadf75a2016-08-22 14:24:24 -07001921 SkScalar rotate = random->nextSScalar1() * 360.f;
1922 SkScalar translateX = random->nextSScalar1() * 1000.f;
1923 SkScalar translateY = random->nextSScalar1() * 1000.f;
1924 SkScalar scale = random->nextSScalar1() * 100.f;
1925 SkMatrix viewMatrix;
1926 viewMatrix.setRotate(rotate);
1927 viewMatrix.postTranslate(translateX, translateY);
1928 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07001929 GrColor color = GrRandomColor(random);
1930 SkRect circle = GrTest::TestSquare(random);
1931 SkPoint center = {circle.centerX(), circle.centerY()};
1932 SkScalar radius = circle.width() / 2.f;
1933 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
1934 CircleBatch::ArcParams arcParamsTmp;
1935 const CircleBatch::ArcParams* arcParams = nullptr;
1936 if (random->nextBool()) {
1937 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
1938 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
1939 arcParams = &arcParamsTmp;
1940 }
1941 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
1942 GrStyle(stroke, nullptr), arcParams);
1943 if (batch) {
1944 return batch;
1945 }
1946 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07001947}
1948
bsalomonabd30f52015-08-13 13:34:48 -07001949DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001950 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1951 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001952 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001953 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001954}
1955
bsalomonabd30f52015-08-13 13:34:48 -07001956DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001957 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1958 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001959 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001960 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001961}
1962
bsalomonabd30f52015-08-13 13:34:48 -07001963DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001964 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1965 GrColor color = GrRandomColor(random);
1966 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07001967 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001968}
1969
1970#endif