blob: d1b8a6b1fb42e122057a36fc2271e052b2f6cbf9 [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"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070018#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080019#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080020#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070021#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080022#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080023#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080024#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080025#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000026
joshualitt76e7fb62015-02-11 08:52:27 -080027// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
28
commit-bot@chromium.org81312832013-03-22 18:34:09 +000029namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080030
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000032 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080033 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000034 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37};
38
39struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000040 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080041 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fOffset;
43 SkPoint fOuterRadii;
44 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000045};
46
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000047struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080049 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000052};
53
commit-bot@chromium.org81312832013-03-22 18:34:09 +000054inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56}
57
58}
59
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060///////////////////////////////////////////////////////////////////////////////
61
62/**
bsalomonce1c8862014-12-15 07:11:22 -080063 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080065 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080066 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080067 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080068 * vec4f : (p.xy, outerRad, innerRad)
69 * p is the position in the normalized space.
70 * outerRad is the outerRadius in device space.
71 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072 */
73
bsalomoncdaa97b2016-03-08 08:30:14 -080074class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000075public:
bsalomoncdaa97b2016-03-08 08:30:14 -080076 CircleGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix){
77 this->initClassID<CircleGeometryProcessor>();
78 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
79 kHigh_GrSLPrecision));
80 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
81 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
82 kVec4f_GrVertexAttribType));
83 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084 }
85
joshualitt71c92602015-01-14 08:12:47 -080086 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -080087 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -080088 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitte3ababe2015-05-15 07:56:07 -070089 const SkMatrix& localMatrix() const { return fLocalMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -080090 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000091
mtklein36352bf2015-03-25 18:17:31 -070092 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000093
egdaniel57d3b032015-11-13 11:57:27 -080094 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095 public:
brianosmanbb2ff942016-02-11 14:15:18 -080096 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -080099 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800100 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800101 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800102 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800103
joshualittabb52a12015-01-13 15:02:10 -0800104 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800105 varyingHandler->emitAttributes(cgp);
joshualittabb52a12015-01-13 15:02:10 -0800106
egdaniel8dcdedc2015-11-11 06:27:20 -0800107 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800108 varyingHandler->addVarying("CircleEdge", &v);
bsalomoncdaa97b2016-03-08 08:30:14 -0800109 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cgp.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110
cdalton85285412016-02-18 12:37:07 -0800111 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700112 // setup pass through color
bsalomoncdaa97b2016-03-08 08:30:14 -0800113 varyingHandler->addPassThroughAttribute(cgp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800114
joshualittabb52a12015-01-13 15:02:10 -0800115 // Setup position
bsalomoncdaa97b2016-03-08 08:30:14 -0800116 this->setupPosition(vertBuilder, gpArgs, cgp.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800117
118 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800119 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800120 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800121 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800122 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800123 cgp.inPosition()->fName,
124 cgp.localMatrix(),
egdaniel4ca2e602015-11-18 08:01:26 -0800125 args.fTransformsIn,
126 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800127
egdaniel4ca2e602015-11-18 08:01:26 -0800128 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
129 fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
130 v.fsIn());
bsalomoncdaa97b2016-03-08 08:30:14 -0800131 if (cgp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800132 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
133 v.fsIn(), v.fsIn());
134 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000135 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000136
egdaniel4ca2e602015-11-18 08:01:26 -0800137 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000138 }
139
robertphillips46d36f02015-01-18 08:14:14 -0800140 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700141 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700142 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800143 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
144 uint16_t key = cgp.fStroke ? 0x1 : 0x0;
145 key |= cgp.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700146 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000147 }
148
egdaniel018fb622015-10-28 07:26:40 -0700149 void setData(const GrGLSLProgramDataManager& pdman,
150 const GrPrimitiveProcessor& gp) override {
joshualitt9b989322014-12-15 14:16:27 -0800151 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152
joshualitte3ababe2015-05-15 07:56:07 -0700153 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700154 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700155 int index,
156 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800157 this->setTransformDataHelper<CircleGeometryProcessor>(primProc, pdman, index,
158 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700159 }
160
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000161 private:
egdaniele659a582015-11-13 09:55:43 -0800162 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000163 };
164
egdaniel57d3b032015-11-13 11:57:27 -0800165 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
166 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800167 }
168
egdaniel57d3b032015-11-13 11:57:27 -0800169 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
170 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800171 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000172
173private:
bsalomoncdaa97b2016-03-08 08:30:14 -0800174 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800175 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800176 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800177 const Attribute* fInCircleEdge;
bsalomoncdaa97b2016-03-08 08:30:14 -0800178 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000179
joshualittb0a8a372014-09-23 09:50:21 -0700180 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000181
joshualitt249af152014-09-15 11:41:13 -0700182 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000183};
184
bsalomoncdaa97b2016-03-08 08:30:14 -0800185GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000186
bsalomoncdaa97b2016-03-08 08:30:14 -0800187const GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
188 return new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189}
190
191///////////////////////////////////////////////////////////////////////////////
192
193/**
194 * 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 +0000195 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
196 * in both x and y directions.
197 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000198 * 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 +0000199 */
200
bsalomoncdaa97b2016-03-08 08:30:14 -0800201class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800203 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
204 : fLocalMatrix(localMatrix) {
205 this->initClassID<EllipseGeometryProcessor>();
206 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
207 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
208 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
209 kVec2f_GrVertexAttribType));
210 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
211 kVec4f_GrVertexAttribType));
212 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000213 }
214
bsalomoncdaa97b2016-03-08 08:30:14 -0800215 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216
mtklein36352bf2015-03-25 18:17:31 -0700217 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800218
joshualitt71c92602015-01-14 08:12:47 -0800219 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800220 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800221 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
222 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitte3ababe2015-05-15 07:56:07 -0700223 const SkMatrix& localMatrix() const { return fLocalMatrix; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
egdaniel57d3b032015-11-13 11:57:27 -0800225 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800227 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228
mtklein36352bf2015-03-25 18:17:31 -0700229 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800230 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800231 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800232 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800233 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234
joshualittabb52a12015-01-13 15:02:10 -0800235 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800236 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800237
egdaniel8dcdedc2015-11-11 06:27:20 -0800238 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800239 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800240 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800241 egp.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242
egdaniel8dcdedc2015-11-11 06:27:20 -0800243 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800244 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800245 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800246 egp.inEllipseRadii()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800247
cdalton85285412016-02-18 12:37:07 -0800248 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700249 // setup pass through color
bsalomoncdaa97b2016-03-08 08:30:14 -0800250 varyingHandler->addPassThroughAttribute(egp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800251
joshualittabb52a12015-01-13 15:02:10 -0800252 // Setup position
bsalomoncdaa97b2016-03-08 08:30:14 -0800253 this->setupPosition(vertBuilder, gpArgs, egp.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800254
255 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800256 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800257 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800258 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800259 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800260 egp.inPosition()->fName,
261 egp.localMatrix(),
egdaniel4ca2e602015-11-18 08:01:26 -0800262 args.fTransformsIn,
263 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800264
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000265 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800266 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
267 ellipseRadii.fsIn());
268 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
269 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
270 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700271
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000272 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800273 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
274 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800275 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000276
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000277 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800278 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800279 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
280 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
281 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
282 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
283 ellipseRadii.fsIn());
284 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
285 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000286 }
287
egdaniel4ca2e602015-11-18 08:01:26 -0800288 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000289 }
290
robertphillips46d36f02015-01-18 08:14:14 -0800291 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700292 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700293 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800294 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
295 uint16_t key = egp.fStroke ? 0x1 : 0x0;
296 key |= egp.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700297 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000298 }
299
egdaniel018fb622015-10-28 07:26:40 -0700300 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000301 }
302
joshualitte3ababe2015-05-15 07:56:07 -0700303 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700304 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700305 int index,
306 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800307 this->setTransformDataHelper<EllipseGeometryProcessor>(primProc, pdman, index,
308 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700309 }
310
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000311 private:
egdaniele659a582015-11-13 09:55:43 -0800312 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000313 };
314
egdaniel57d3b032015-11-13 11:57:27 -0800315 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
316 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800317 }
318
egdaniel57d3b032015-11-13 11:57:27 -0800319 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
320 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800321 }
322
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000323private:
joshualitt71c92602015-01-14 08:12:47 -0800324 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800325 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800326 const Attribute* fInEllipseOffset;
327 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700328 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000329 bool fStroke;
330
joshualittb0a8a372014-09-23 09:50:21 -0700331 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000332
joshualitt249af152014-09-15 11:41:13 -0700333 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000334};
335
bsalomoncdaa97b2016-03-08 08:30:14 -0800336GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000337
bsalomoncdaa97b2016-03-08 08:30:14 -0800338const GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
339 return new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000340}
341
342///////////////////////////////////////////////////////////////////////////////
343
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000344/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000345 * 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 +0000346 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
347 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
348 * using differentials.
349 *
350 * The result is device-independent and can be used with any affine matrix.
351 */
352
bsalomoncdaa97b2016-03-08 08:30:14 -0800353enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000354
bsalomoncdaa97b2016-03-08 08:30:14 -0800355class DIEllipseGeometryProcessor : public GrGeometryProcessor {
356public:
357 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
358 : fViewMatrix(viewMatrix) {
359 this->initClassID<DIEllipseGeometryProcessor>();
360 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
361 kHigh_GrSLPrecision));
362 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
363 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
364 kVec2f_GrVertexAttribType));
365 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
366 kVec2f_GrVertexAttribType));
367 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000368 }
369
bsalomoncdaa97b2016-03-08 08:30:14 -0800370
371 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000372
mtklein36352bf2015-03-25 18:17:31 -0700373 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000374
joshualitt71c92602015-01-14 08:12:47 -0800375 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800376 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800377 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
378 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitte578a952015-05-14 10:09:13 -0700379 const SkMatrix& viewMatrix() const { return fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -0800380
egdaniel57d3b032015-11-13 11:57:27 -0800381 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000382 public:
egdaniel57d3b032015-11-13 11:57:27 -0800383 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800384 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000385
joshualitt465283c2015-09-11 08:19:35 -0700386 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800387 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800388 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800389 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800390 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000391
joshualittabb52a12015-01-13 15:02:10 -0800392 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800393 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800394
egdaniel8dcdedc2015-11-11 06:27:20 -0800395 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800396 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800397 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800398 diegp.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700399
egdaniel8dcdedc2015-11-11 06:27:20 -0800400 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800401 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800402 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800403 diegp.inEllipseOffsets1()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800404
cdalton85285412016-02-18 12:37:07 -0800405 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomoncdaa97b2016-03-08 08:30:14 -0800406 varyingHandler->addPassThroughAttribute(diegp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800407
joshualittabb52a12015-01-13 15:02:10 -0800408 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800409 this->setupPosition(vertBuilder,
410 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800411 gpArgs,
bsalomoncdaa97b2016-03-08 08:30:14 -0800412 diegp.inPosition()->fName,
413 diegp.viewMatrix(),
joshualitt5559ca22015-05-21 15:50:36 -0700414 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800415
416 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800417 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800418 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800419 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800420 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800421 diegp.inPosition()->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800422 args.fTransformsIn,
423 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800424
egdaniel4ca2e602015-11-18 08:01:26 -0800425 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800426 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800428 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
429 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
430 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
431 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
432 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
433 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800434 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
435 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000436
egdaniel4ca2e602015-11-18 08:01:26 -0800437 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000438 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800439 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
440 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800441 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000442 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800443 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
444 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000445 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800446 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000447 }
448
449 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800450 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800451 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
452 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
453 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
454 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
455 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
456 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
457 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
458 offsets1.fsIn());
459 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
460 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000461 }
462
egdaniel4ca2e602015-11-18 08:01:26 -0800463 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000464 }
465
robertphillips46d36f02015-01-18 08:14:14 -0800466 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700467 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700468 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800469 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
470 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
471 key |= ComputePosKey(diegp.viewMatrix()) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700472 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000473 }
474
egdaniel018fb622015-10-28 07:26:40 -0700475 void setData(const GrGLSLProgramDataManager& pdman,
476 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800477 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700478
bsalomoncdaa97b2016-03-08 08:30:14 -0800479 if (!diegp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(diegp.viewMatrix())) {
480 fViewMatrix = diegp.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700481 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800482 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700483 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
484 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000485 }
486
487 private:
joshualitt5559ca22015-05-21 15:50:36 -0700488 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700489 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800490
egdaniele659a582015-11-13 09:55:43 -0800491 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000492 };
493
egdaniel57d3b032015-11-13 11:57:27 -0800494 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
495 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800496 }
497
egdaniel57d3b032015-11-13 11:57:27 -0800498 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
499 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800500 }
501
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000502private:
joshualitt71c92602015-01-14 08:12:47 -0800503 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800504 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800505 const Attribute* fInEllipseOffsets0;
506 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800507 SkMatrix fViewMatrix;
508 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000509
joshualittb0a8a372014-09-23 09:50:21 -0700510 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000511
joshualitt249af152014-09-15 11:41:13 -0700512 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513};
514
bsalomoncdaa97b2016-03-08 08:30:14 -0800515GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000516
bsalomoncdaa97b2016-03-08 08:30:14 -0800517const GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
518 return new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
519 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000520}
521
522///////////////////////////////////////////////////////////////////////////////
523
robertphillipsb56f9272016-02-25 11:03:52 -0800524GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -0800525 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -0800526 const SkRect& oval,
527 const SkStrokeRec& stroke,
528 GrShaderCaps* shaderCaps) {
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000529 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800530 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillips0cc2f852016-02-24 13:36:56 -0800531 return CreateCircleBatch(color, viewMatrix, oval, stroke);
532 }
533
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534 // if we have shader derivative support, render as device-independent
robertphillips0cc2f852016-02-24 13:36:56 -0800535 if (shaderCaps->shaderDerivativeSupport()) {
536 return CreateDIEllipseBatch(color, viewMatrix, oval, stroke);
537 }
538
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000539 // otherwise axis-aligned ellipses only
robertphillips0cc2f852016-02-24 13:36:56 -0800540 if (viewMatrix.rectStaysRect()) {
541 return CreateEllipseBatch(color, viewMatrix, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000542 }
543
robertphillips0cc2f852016-02-24 13:36:56 -0800544 return nullptr;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000545}
546
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000547///////////////////////////////////////////////////////////////////////////////
548
bsalomonabd30f52015-08-13 13:34:48 -0700549class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800550public:
reed1b55a962015-09-17 20:16:13 -0700551 DEFINE_BATCH_CLASS_ID
552
joshualitt76e7fb62015-02-11 08:52:27 -0800553 struct Geometry {
reed1b55a962015-09-17 20:16:13 -0700554 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800555 SkScalar fInnerRadius;
556 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700557 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800558 };
559
bsalomoncdaa97b2016-03-08 08:30:14 -0800560 CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
561 : INHERITED(ClassID())
562 , fStroked(stroked)
563 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
564 fGeoData.push_back(geometry);
565 this->setBounds(geometry.fDevBounds);
566 }
mtklein36352bf2015-03-25 18:17:31 -0700567 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800568
robertphillipse004bfc2015-11-16 09:06:59 -0800569 SkString dumpInfo() const override {
570 SkString string;
571 for (int i = 0; i < fGeoData.count(); ++i) {
572 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
573 "InnerRad: %.2f, OuterRad: %.2f\n",
574 fGeoData[i].fColor,
575 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
576 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
577 fGeoData[i].fInnerRadius,
578 fGeoData[i].fOuterRadius);
579 }
580 string.append(INHERITED::dumpInfo());
581 return string;
582 }
583
ethannicholasff210322015-11-24 12:10:10 -0800584 void computePipelineOptimizations(GrInitInvariantOutput* color,
585 GrInitInvariantOutput* coverage,
586 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800587 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800588 color->setKnownFourComponents(fGeoData[0].fColor);
589 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800590 }
591
bsalomone46f9fe2015-08-18 06:05:14 -0700592private:
ethannicholasff210322015-11-24 12:10:10 -0800593 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800594 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800595 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800596 if (!overrides.readsLocalCoords()) {
597 fViewMatrixIfUsingLocalCoords.reset();
598 }
joshualitt76e7fb62015-02-11 08:52:27 -0800599 }
600
joshualitt144c3c82015-11-30 12:30:13 -0800601 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800602 SkMatrix localMatrix;
603 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800604 return;
605 }
606
607 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800608 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800609
bsalomon75398562015-08-17 12:55:38 -0700610 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800611
joshualitt76e7fb62015-02-11 08:52:27 -0800612 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800613 size_t vertexStride = gp->getVertexStride();
614 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700615 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700616 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700617 instanceCount));
618 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800619 return;
620 }
621
joshualitt76e7fb62015-02-11 08:52:27 -0800622 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800623 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800624
brianosmanbb2ff942016-02-11 14:15:18 -0800625 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700626 SkScalar innerRadius = geom.fInnerRadius;
627 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800628
bsalomonb5238a72015-05-05 07:49:49 -0700629 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800630
631 // The inner radius in the vertex data must be specified in normalized space.
632 innerRadius = innerRadius / outerRadius;
633 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800634 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800635 verts[0].fOffset = SkPoint::Make(-1, -1);
636 verts[0].fOuterRadius = outerRadius;
637 verts[0].fInnerRadius = innerRadius;
638
639 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800640 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800641 verts[1].fOffset = SkPoint::Make(-1, 1);
642 verts[1].fOuterRadius = outerRadius;
643 verts[1].fInnerRadius = innerRadius;
644
645 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800646 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800647 verts[2].fOffset = SkPoint::Make(1, 1);
648 verts[2].fOuterRadius = outerRadius;
649 verts[2].fInnerRadius = innerRadius;
650
651 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800652 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800653 verts[3].fOffset = SkPoint::Make(1, -1);
654 verts[3].fOuterRadius = outerRadius;
655 verts[3].fInnerRadius = innerRadius;
656
bsalomonb5238a72015-05-05 07:49:49 -0700657 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800658 }
bsalomon75398562015-08-17 12:55:38 -0700659 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800660 }
661
bsalomoncb02b382015-08-12 11:14:50 -0700662 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700663 CircleBatch* that = t->cast<CircleBatch>();
664 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
665 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700666 return false;
667 }
668
bsalomoncdaa97b2016-03-08 08:30:14 -0800669 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800670 return false;
671 }
672
bsalomoncdaa97b2016-03-08 08:30:14 -0800673 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800674 return false;
675 }
676
bsalomoncdaa97b2016-03-08 08:30:14 -0800677 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -0700678 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800679 return true;
680 }
681
bsalomoncdaa97b2016-03-08 08:30:14 -0800682 bool fStroked;
683 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800684 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700685
686 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800687};
688
bsalomonabd30f52015-08-13 13:34:48 -0700689static GrDrawBatch* create_circle_batch(GrColor color,
690 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -0700691 const SkRect& circle,
692 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000693 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800694 viewMatrix.mapPoints(&center, 1);
695 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
696 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000697
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000698 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000699 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
700 SkStrokeRec::kHairline_Style == style;
701 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000702
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000703 SkScalar innerRadius = 0.0f;
704 SkScalar outerRadius = radius;
705 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000706 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000707 if (SkScalarNearlyZero(strokeWidth)) {
708 halfWidth = SK_ScalarHalf;
709 } else {
710 halfWidth = SkScalarHalf(strokeWidth);
711 }
712
713 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000714 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000715 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000716 }
717 }
718
bsalomonce1c8862014-12-15 07:11:22 -0800719 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
720 // computation because the computed alpha is zero, rather than 50%, at the radius.
721 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
722 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000723 outerRadius += SK_ScalarHalf;
724 innerRadius -= SK_ScalarHalf;
725
joshualitt76e7fb62015-02-11 08:52:27 -0800726 CircleBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -0800727 geometry.fColor = color;
728 geometry.fInnerRadius = innerRadius;
729 geometry.fOuterRadius = outerRadius;
joshualittd96a67b2015-05-05 14:09:05 -0700730 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
731 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000732
bsalomoncdaa97b2016-03-08 08:30:14 -0800733 return new CircleBatch(geometry, viewMatrix, isStrokeOnly && innerRadius > 0);
joshualitt3e708c52015-04-30 13:49:27 -0700734}
735
robertphillips0cc2f852016-02-24 13:36:56 -0800736GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color,
737 const SkMatrix& viewMatrix,
738 const SkRect& circle,
739 const SkStrokeRec& stroke) {
740 return create_circle_batch(color, viewMatrix, circle, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000741}
742
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000743///////////////////////////////////////////////////////////////////////////////
744
bsalomonabd30f52015-08-13 13:34:48 -0700745class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800746public:
reed1b55a962015-09-17 20:16:13 -0700747 DEFINE_BATCH_CLASS_ID
748
joshualitt76e7fb62015-02-11 08:52:27 -0800749 struct Geometry {
reed1b55a962015-09-17 20:16:13 -0700750 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800751 SkScalar fXRadius;
752 SkScalar fYRadius;
753 SkScalar fInnerXRadius;
754 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700755 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800756 };
757
bsalomoncdaa97b2016-03-08 08:30:14 -0800758 EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
759 : INHERITED(ClassID())
760 , fStroked(stroked)
761 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
762 fGeoData.push_back(geometry);
763 this->setBounds(geometry.fDevBounds);
764 }
joshualitt76e7fb62015-02-11 08:52:27 -0800765
mtklein36352bf2015-03-25 18:17:31 -0700766 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800767
ethannicholasff210322015-11-24 12:10:10 -0800768 void computePipelineOptimizations(GrInitInvariantOutput* color,
769 GrInitInvariantOutput* coverage,
770 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800771 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800772 color->setKnownFourComponents(fGeoData[0].fColor);
773 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800774 }
775
bsalomone46f9fe2015-08-18 06:05:14 -0700776private:
ethannicholasff210322015-11-24 12:10:10 -0800777 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800778 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800779 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800780 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800781 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800782 if (!overrides.readsLocalCoords()) {
783 fViewMatrixIfUsingLocalCoords.reset();
784 }
joshualitt76e7fb62015-02-11 08:52:27 -0800785 }
786
joshualitt144c3c82015-11-30 12:30:13 -0800787 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800788 SkMatrix localMatrix;
789 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800790 return;
791 }
792
793 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800794 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800795
bsalomon75398562015-08-17 12:55:38 -0700796 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800797
joshualitt76e7fb62015-02-11 08:52:27 -0800798 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700799 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800800 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800801 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700802 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700803 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700804 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800805 return;
806 }
807
bsalomon8415abe2015-05-04 11:41:41 -0700808 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800809 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700810
brianosmanbb2ff942016-02-11 14:15:18 -0800811 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700812 SkScalar xRadius = geom.fXRadius;
813 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800814
815 // Compute the reciprocals of the radii here to save time in the shader
816 SkScalar xRadRecip = SkScalarInvert(xRadius);
817 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700818 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
819 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800820
bsalomonb5238a72015-05-05 07:49:49 -0700821 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800822
823 // The inner radius in the vertex data must be specified in normalized space.
824 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800825 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800826 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
827 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
828 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
829
830 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800831 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800832 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
833 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
834 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
835
836 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800837 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800838 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
839 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
840 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
841
842 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800843 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800844 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
845 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
846 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
847
bsalomonb5238a72015-05-05 07:49:49 -0700848 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800849 }
bsalomon75398562015-08-17 12:55:38 -0700850 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800851 }
852
bsalomoncb02b382015-08-12 11:14:50 -0700853 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700854 EllipseBatch* that = t->cast<EllipseBatch>();
855
856 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
857 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700858 return false;
859 }
860
bsalomoncdaa97b2016-03-08 08:30:14 -0800861 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800862 return false;
863 }
864
bsalomoncdaa97b2016-03-08 08:30:14 -0800865 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800866 return false;
867 }
868
bsalomoncdaa97b2016-03-08 08:30:14 -0800869 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -0700870 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800871 return true;
872 }
873
joshualitt76e7fb62015-02-11 08:52:27 -0800874
bsalomoncdaa97b2016-03-08 08:30:14 -0800875 bool fStroked;
876 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800877 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700878
879 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800880};
881
bsalomonabd30f52015-08-13 13:34:48 -0700882static GrDrawBatch* create_ellipse_batch(GrColor color,
883 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -0700884 const SkRect& ellipse,
885 const SkStrokeRec& stroke) {
robertphillips0cc2f852016-02-24 13:36:56 -0800886 SkASSERT(viewMatrix.rectStaysRect());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000887
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000888 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000889 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800890 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000891 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
892 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -0800893 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
894 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
895 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
896 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000897
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000898 // do (potentially) anisotropic mapping of stroke
899 SkVector scaledStroke;
900 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -0800901 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
902 viewMatrix[SkMatrix::kMSkewY]));
903 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
904 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000905
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000906 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000907 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
908 SkStrokeRec::kHairline_Style == style;
909 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000910
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000911 SkScalar innerXRadius = 0;
912 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000913 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000914 if (SkScalarNearlyZero(scaledStroke.length())) {
915 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
916 } else {
917 scaledStroke.scale(SK_ScalarHalf);
918 }
919
920 // we only handle thick strokes for near-circular ellipses
921 if (scaledStroke.length() > SK_ScalarHalf &&
922 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700923 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000924 }
925
926 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
927 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
928 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -0700929 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000930 }
931
932 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000933 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000934 innerXRadius = xRadius - scaledStroke.fX;
935 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000936 }
937
938 xRadius += scaledStroke.fX;
939 yRadius += scaledStroke.fY;
940 }
941
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000942 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000943 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000944 // TODO: Consider if we should use sqrt(2)/2 instead
brianosmanbd6366a2016-02-14 10:33:03 -0800945 xRadius += SK_ScalarHalf;
946 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000947
joshualitt76e7fb62015-02-11 08:52:27 -0800948 EllipseBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -0800949 geometry.fColor = color;
950 geometry.fXRadius = xRadius;
951 geometry.fYRadius = yRadius;
952 geometry.fInnerXRadius = innerXRadius;
953 geometry.fInnerYRadius = innerYRadius;
brianosmanbd6366a2016-02-14 10:33:03 -0800954 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
955 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000956
bsalomoncdaa97b2016-03-08 08:30:14 -0800957 return new EllipseBatch(geometry, viewMatrix,
958 isStrokeOnly && innerXRadius > 0 && innerYRadius > 0);
joshualitt3e708c52015-04-30 13:49:27 -0700959}
960
robertphillips0cc2f852016-02-24 13:36:56 -0800961GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color,
962 const SkMatrix& viewMatrix,
963 const SkRect& ellipse,
964 const SkStrokeRec& stroke) {
965 return create_ellipse_batch(color, viewMatrix, ellipse, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000966}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000967
joshualitt76e7fb62015-02-11 08:52:27 -0800968/////////////////////////////////////////////////////////////////////////////////////////////////
969
bsalomonabd30f52015-08-13 13:34:48 -0700970class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800971public:
reed1b55a962015-09-17 20:16:13 -0700972 DEFINE_BATCH_CLASS_ID
973
joshualitt76e7fb62015-02-11 08:52:27 -0800974 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800975 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700976 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800977 SkScalar fXRadius;
978 SkScalar fYRadius;
979 SkScalar fInnerXRadius;
980 SkScalar fInnerYRadius;
981 SkScalar fGeoDx;
982 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -0700983 GrColor fColor;
bsalomoncdaa97b2016-03-08 08:30:14 -0800984 DIEllipseStyle fStyle;
joshualitt76e7fb62015-02-11 08:52:27 -0800985 };
986
bsalomonabd30f52015-08-13 13:34:48 -0700987 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -0700988 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800989 }
990
mtklein36352bf2015-03-25 18:17:31 -0700991 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800992
ethannicholasff210322015-11-24 12:10:10 -0800993 void computePipelineOptimizations(GrInitInvariantOutput* color,
994 GrInitInvariantOutput* coverage,
995 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800996 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800997 color->setKnownFourComponents(fGeoData[0].fColor);
998 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800999 }
1000
bsalomone46f9fe2015-08-18 06:05:14 -07001001private:
1002
ethannicholasff210322015-11-24 12:10:10 -08001003 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001004 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001005 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001006 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001007 }
1008
joshualitt144c3c82015-11-30 12:30:13 -08001009 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001010 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001011 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1012 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001013
bsalomon75398562015-08-17 12:55:38 -07001014 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001015
joshualitt76e7fb62015-02-11 08:52:27 -08001016 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001017 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001018 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001019 QuadHelper helper;
1020 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001021 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001022 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001023 return;
1024 }
1025
joshualitt76e7fb62015-02-11 08:52:27 -08001026 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001027 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001028
brianosmanbb2ff942016-02-11 14:15:18 -08001029 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001030 SkScalar xRadius = geom.fXRadius;
1031 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001032
bsalomonb5238a72015-05-05 07:49:49 -07001033 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001034
1035 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001036 SkScalar offsetDx = geom.fGeoDx / xRadius;
1037 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001038
reed80ea19c2015-05-12 10:37:34 -07001039 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1040 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001041
1042 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001043 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001044 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1045 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1046
1047 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001048 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001049 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1050 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1051
1052 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001053 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001054 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1055 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1056
1057 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001058 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001059 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1060 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1061
bsalomonb5238a72015-05-05 07:49:49 -07001062 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001063 }
bsalomon75398562015-08-17 12:55:38 -07001064 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001065 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001066
reed1b55a962015-09-17 20:16:13 -07001067 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001068 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001069
1070 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001071 }
1072
bsalomoncb02b382015-08-12 11:14:50 -07001073 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001074 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1075 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1076 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001077 return false;
1078 }
1079
bsalomoncdaa97b2016-03-08 08:30:14 -08001080 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001081 return false;
1082 }
1083
joshualittd96a67b2015-05-05 14:09:05 -07001084 // TODO rewrite to allow positioning on CPU
1085 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001086 return false;
1087 }
1088
bsalomoncdaa97b2016-03-08 08:30:14 -08001089 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001090 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001091 return true;
1092 }
1093
joshualitt76e7fb62015-02-11 08:52:27 -08001094 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001095 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001096
bsalomoncdaa97b2016-03-08 08:30:14 -08001097 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001098 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001099
1100 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001101};
1102
bsalomonabd30f52015-08-13 13:34:48 -07001103static GrDrawBatch* create_diellipse_batch(GrColor color,
1104 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -07001105 const SkRect& ellipse,
1106 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001107 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001108 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001109 SkScalar yRadius = SkScalarHalf(ellipse.height());
1110
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001111 SkStrokeRec::Style style = stroke.getStyle();
bsalomoncdaa97b2016-03-08 08:30:14 -08001112 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1113 DIEllipseStyle::kStroke :
1114 (SkStrokeRec::kHairline_Style == style) ?
1115 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001116
1117 SkScalar innerXRadius = 0;
1118 SkScalar innerYRadius = 0;
1119 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1120 SkScalar strokeWidth = stroke.getWidth();
1121
1122 if (SkScalarNearlyZero(strokeWidth)) {
1123 strokeWidth = SK_ScalarHalf;
1124 } else {
1125 strokeWidth *= SK_ScalarHalf;
1126 }
1127
1128 // we only handle thick strokes for near-circular ellipses
1129 if (strokeWidth > SK_ScalarHalf &&
1130 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001131 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001132 }
1133
1134 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1135 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1136 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001137 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001138 }
1139
1140 // set inner radius (if needed)
1141 if (SkStrokeRec::kStroke_Style == style) {
1142 innerXRadius = xRadius - strokeWidth;
1143 innerYRadius = yRadius - strokeWidth;
1144 }
1145
1146 xRadius += strokeWidth;
1147 yRadius += strokeWidth;
1148 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001149 if (DIEllipseStyle::kStroke == dieStyle) {
1150 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1151 DIEllipseStyle ::kFill;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001152 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001153
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001154 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001155 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1156 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1157 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1158 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001159 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1160 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001161
joshualitt76e7fb62015-02-11 08:52:27 -08001162 DIEllipseBatch::Geometry geometry;
1163 geometry.fViewMatrix = viewMatrix;
1164 geometry.fColor = color;
1165 geometry.fXRadius = xRadius;
1166 geometry.fYRadius = yRadius;
1167 geometry.fInnerXRadius = innerXRadius;
1168 geometry.fInnerYRadius = innerYRadius;
1169 geometry.fGeoDx = geoDx;
1170 geometry.fGeoDy = geoDy;
bsalomoncdaa97b2016-03-08 08:30:14 -08001171 geometry.fStyle = dieStyle;
joshualittd96a67b2015-05-05 14:09:05 -07001172 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1173 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001174
joshualittd96a67b2015-05-05 14:09:05 -07001175 SkRect devBounds = geometry.fBounds;
1176 viewMatrix.mapRect(&devBounds);
1177 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001178}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001179
robertphillips0cc2f852016-02-24 13:36:56 -08001180GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color,
1181 const SkMatrix& viewMatrix,
1182 const SkRect& ellipse,
1183 const SkStrokeRec& stroke) {
1184 return create_diellipse_batch(color, viewMatrix, ellipse, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001185}
1186
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001187///////////////////////////////////////////////////////////////////////////////
1188
1189static const uint16_t gRRectIndices[] = {
1190 // corners
1191 0, 1, 5, 0, 5, 4,
1192 2, 3, 7, 2, 7, 6,
1193 8, 9, 13, 8, 13, 12,
1194 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001195
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001196 // edges
1197 1, 2, 6, 1, 6, 5,
1198 4, 5, 9, 4, 9, 8,
1199 6, 7, 11, 6, 11, 10,
1200 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001201
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001202 // center
1203 // we place this at the end so that we can ignore these indices when rendering stroke-only
1204 5, 6, 10, 5, 10, 9
1205};
1206
joshualitt5ead6da2014-10-22 16:00:29 -07001207static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1208static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1209static const int kVertsPerRRect = 16;
1210static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001211
bsalomoned0bcad2015-05-04 10:36:42 -07001212GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1213GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1214static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1215 GrResourceProvider* resourceProvider) {
1216 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1217 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1218 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001219 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001220 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1221 gStrokeRRectOnlyIndexBufferKey);
1222 } else {
bsalomoneae62002015-07-31 13:59:30 -07001223 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001224 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1225 gRRectOnlyIndexBufferKey);
1226
1227 }
1228}
1229
joshualitt76e7fb62015-02-11 08:52:27 -08001230///////////////////////////////////////////////////////////////////////////////////////////////////
1231
bsalomonabd30f52015-08-13 13:34:48 -07001232class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001233public:
reed1b55a962015-09-17 20:16:13 -07001234 DEFINE_BATCH_CLASS_ID
1235
joshualitt76e7fb62015-02-11 08:52:27 -08001236 struct Geometry {
reed1b55a962015-09-17 20:16:13 -07001237 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001238 SkScalar fInnerRadius;
1239 SkScalar fOuterRadius;
bsalomoncdaa97b2016-03-08 08:30:14 -08001240 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001241 };
1242
bsalomoncdaa97b2016-03-08 08:30:14 -08001243 RRectCircleRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
1244 : INHERITED(ClassID())
1245 , fStroked(stroked)
1246 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1247 fGeoData.push_back(geometry);
1248
1249 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001250 }
1251
mtklein36352bf2015-03-25 18:17:31 -07001252 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001253
ethannicholasff210322015-11-24 12:10:10 -08001254 void computePipelineOptimizations(GrInitInvariantOutput* color,
1255 GrInitInvariantOutput* coverage,
1256 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001257 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001258 color->setKnownFourComponents(fGeoData[0].fColor);
1259 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001260 }
1261
bsalomone46f9fe2015-08-18 06:05:14 -07001262private:
ethannicholasff210322015-11-24 12:10:10 -08001263 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001264 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001265 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001266 if (!overrides.readsLocalCoords()) {
1267 fViewMatrixIfUsingLocalCoords.reset();
1268 }
joshualitt76e7fb62015-02-11 08:52:27 -08001269 }
1270
joshualitt144c3c82015-11-30 12:30:13 -08001271 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001272 // Invert the view matrix as a local matrix (if any other processors require coords).
1273 SkMatrix localMatrix;
1274 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001275 return;
1276 }
1277
1278 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001279 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001280
bsalomon75398562015-08-17 12:55:38 -07001281 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001282
joshualitt76e7fb62015-02-11 08:52:27 -08001283 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001284 size_t vertexStride = gp->getVertexStride();
1285 SkASSERT(vertexStride == sizeof(CircleVertex));
1286
bsalomonb5238a72015-05-05 07:49:49 -07001287 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001288 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001289 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001290 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001291
bsalomonb5238a72015-05-05 07:49:49 -07001292 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001293 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001294 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1295 indicesPerInstance, instanceCount));
1296 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001297 SkDebugf("Could not allocate vertices\n");
1298 return;
1299 }
1300
joshualitt76e7fb62015-02-11 08:52:27 -08001301 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001302 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001303
brianosmanbb2ff942016-02-11 14:15:18 -08001304 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001305 SkScalar outerRadius = args.fOuterRadius;
1306
egdanielbc227142015-04-21 06:28:08 -07001307 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001308
1309 SkScalar yCoords[4] = {
1310 bounds.fTop,
1311 bounds.fTop + outerRadius,
1312 bounds.fBottom - outerRadius,
1313 bounds.fBottom
1314 };
1315
1316 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1317 // The inner radius in the vertex data must be specified in normalized space.
1318 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1319 for (int i = 0; i < 4; ++i) {
1320 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001321 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001322 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1323 verts->fOuterRadius = outerRadius;
1324 verts->fInnerRadius = innerRadius;
1325 verts++;
1326
1327 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001328 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001329 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1330 verts->fOuterRadius = outerRadius;
1331 verts->fInnerRadius = innerRadius;
1332 verts++;
1333
1334 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001335 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001336 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1337 verts->fOuterRadius = outerRadius;
1338 verts->fInnerRadius = innerRadius;
1339 verts++;
1340
1341 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001342 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001343 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1344 verts->fOuterRadius = outerRadius;
1345 verts->fInnerRadius = innerRadius;
1346 verts++;
1347 }
1348 }
1349
bsalomon75398562015-08-17 12:55:38 -07001350 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001351 }
1352
bsalomoncb02b382015-08-12 11:14:50 -07001353 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001354 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1355 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1356 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001357 return false;
1358 }
1359
bsalomoncdaa97b2016-03-08 08:30:14 -08001360 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001361 return false;
1362 }
1363
bsalomoncdaa97b2016-03-08 08:30:14 -08001364 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001365 return false;
1366 }
1367
bsalomoncdaa97b2016-03-08 08:30:14 -08001368 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001369 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001370 return true;
1371 }
1372
bsalomoncdaa97b2016-03-08 08:30:14 -08001373 bool fStroked;
1374 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001375 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001376
1377 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001378};
1379
bsalomonabd30f52015-08-13 13:34:48 -07001380class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001381public:
reed1b55a962015-09-17 20:16:13 -07001382 DEFINE_BATCH_CLASS_ID
1383
joshualitt76e7fb62015-02-11 08:52:27 -08001384 struct Geometry {
reed1b55a962015-09-17 20:16:13 -07001385 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001386 SkScalar fXRadius;
1387 SkScalar fYRadius;
1388 SkScalar fInnerXRadius;
1389 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001390 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001391 };
1392
bsalomoncdaa97b2016-03-08 08:30:14 -08001393 RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
1394 : INHERITED(ClassID())
1395 , fStroked(stroked)
1396 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1397 fGeoData.push_back(geometry);
1398 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001399 }
1400
mtklein36352bf2015-03-25 18:17:31 -07001401 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001402
ethannicholasff210322015-11-24 12:10:10 -08001403 void computePipelineOptimizations(GrInitInvariantOutput* color,
1404 GrInitInvariantOutput* coverage,
1405 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001406 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001407 color->setKnownFourComponents(fGeoData[0].fColor);
1408 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001409 }
1410
bsalomone46f9fe2015-08-18 06:05:14 -07001411private:
ethannicholasff210322015-11-24 12:10:10 -08001412 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001413 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001414 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001415 if (!overrides.readsLocalCoords()) {
1416 fViewMatrixIfUsingLocalCoords.reset();
1417 }
joshualitt76e7fb62015-02-11 08:52:27 -08001418 }
1419
joshualitt144c3c82015-11-30 12:30:13 -08001420 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001421 SkMatrix localMatrix;
1422 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001423 return;
1424 }
1425
1426 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001427 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001428
bsalomon75398562015-08-17 12:55:38 -07001429 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001430
joshualitt76e7fb62015-02-11 08:52:27 -08001431 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001432 size_t vertexStride = gp->getVertexStride();
1433 SkASSERT(vertexStride == sizeof(EllipseVertex));
1434
bsalomonb5238a72015-05-05 07:49:49 -07001435 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001436 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001437 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001438 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001439
bsalomonb5238a72015-05-05 07:49:49 -07001440 InstancedHelper helper;
1441 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001442 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001443 kVertsPerRRect, indicesPerInstance, instanceCount));
1444 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001445 SkDebugf("Could not allocate vertices\n");
1446 return;
1447 }
1448
joshualitt76e7fb62015-02-11 08:52:27 -08001449 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001450 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001451
brianosmanbb2ff942016-02-11 14:15:18 -08001452 GrColor color = args.fColor;
1453
joshualitt76e7fb62015-02-11 08:52:27 -08001454 // Compute the reciprocals of the radii here to save time in the shader
1455 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1456 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1457 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1458 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1459
1460 // Extend the radii out half a pixel to antialias.
1461 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1462 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1463
egdanielbc227142015-04-21 06:28:08 -07001464 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001465
1466 SkScalar yCoords[4] = {
1467 bounds.fTop,
1468 bounds.fTop + yOuterRadius,
1469 bounds.fBottom - yOuterRadius,
1470 bounds.fBottom
1471 };
1472 SkScalar yOuterOffsets[4] = {
1473 yOuterRadius,
1474 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1475 SK_ScalarNearlyZero,
1476 yOuterRadius
1477 };
1478
1479 for (int i = 0; i < 4; ++i) {
1480 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001481 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001482 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1483 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1484 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1485 verts++;
1486
1487 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001488 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001489 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1490 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1491 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1492 verts++;
1493
1494 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001495 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001496 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1497 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1498 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1499 verts++;
1500
1501 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001502 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001503 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1504 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1505 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1506 verts++;
1507 }
1508 }
bsalomon75398562015-08-17 12:55:38 -07001509 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001510 }
1511
bsalomoncb02b382015-08-12 11:14:50 -07001512 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001513 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1514
1515 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1516 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001517 return false;
1518 }
1519
bsalomoncdaa97b2016-03-08 08:30:14 -08001520 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001521 return false;
1522 }
1523
bsalomoncdaa97b2016-03-08 08:30:14 -08001524 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001525 return false;
1526 }
1527
bsalomoncdaa97b2016-03-08 08:30:14 -08001528 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001529 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001530 return true;
1531 }
1532
bsalomoncdaa97b2016-03-08 08:30:14 -08001533 bool fStroked;
1534 SkMatrix fViewMatrixIfUsingLocalCoords;
1535 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001536
1537 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001538};
1539
bsalomonabd30f52015-08-13 13:34:48 -07001540static GrDrawBatch* create_rrect_batch(GrColor color,
1541 const SkMatrix& viewMatrix,
1542 const SkRRect& rrect,
1543 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001544 SkASSERT(viewMatrix.rectStaysRect());
1545 SkASSERT(rrect.isSimple());
1546 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001547
joshualitt3e708c52015-04-30 13:49:27 -07001548 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001549 // do any matrix crunching before we reset the draw state for device coords
1550 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001551 SkRect bounds;
1552 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001553
1554 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001555 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1556 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1557 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1558 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001559
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001560 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001561
1562 // do (potentially) anisotropic mapping of stroke
1563 SkVector scaledStroke;
1564 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001565
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001566 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1567 SkStrokeRec::kHairline_Style == style;
1568 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1569
1570 if (hasStroke) {
1571 if (SkStrokeRec::kHairline_Style == style) {
1572 scaledStroke.set(1, 1);
1573 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001574 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1575 viewMatrix[SkMatrix::kMSkewY]));
1576 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1577 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001578 }
1579
1580 // if half of strokewidth is greater than radius, we don't handle that right now
1581 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001582 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001583 }
1584 }
1585
1586 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1587 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1588 // patch will have fractional coverage. This only matters when the interior is actually filled.
1589 // We could consider falling back to rect rendering here, since a tiny radius is
1590 // indistinguishable from a square corner.
1591 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001592 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001593 }
1594
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001595 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001596 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001597 SkScalar innerRadius = 0.0f;
1598 SkScalar outerRadius = xRadius;
1599 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001600 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001601 if (SkScalarNearlyZero(scaledStroke.fX)) {
1602 halfWidth = SK_ScalarHalf;
1603 } else {
1604 halfWidth = SkScalarHalf(scaledStroke.fX);
1605 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001606
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001607 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001608 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001609 }
1610 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001611 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001612 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001613
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001614 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001615
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001616 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001617 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1618 // Second, the outer radius is used to compute the verts of the bounding box that is
1619 // rendered and the outset ensures the box will cover all partially covered by the rrect
1620 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001621 outerRadius += SK_ScalarHalf;
1622 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001623
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001624 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001625 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001626
joshualitt76e7fb62015-02-11 08:52:27 -08001627 RRectCircleRendererBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -08001628 geometry.fColor = color;
1629 geometry.fInnerRadius = innerRadius;
1630 geometry.fOuterRadius = outerRadius;
joshualittd96a67b2015-05-05 14:09:05 -07001631 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001632
bsalomoncdaa97b2016-03-08 08:30:14 -08001633 return new RRectCircleRendererBatch(geometry, viewMatrix, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001634 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001635 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001636 SkScalar innerXRadius = 0.0f;
1637 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001638 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001639 if (SkScalarNearlyZero(scaledStroke.length())) {
1640 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1641 } else {
1642 scaledStroke.scale(SK_ScalarHalf);
1643 }
1644
1645 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001646 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001647 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001648 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001649 }
1650
1651 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1652 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1653 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001654 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001655 }
1656
1657 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001658 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001659 innerXRadius = xRadius - scaledStroke.fX;
1660 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001661 }
1662
1663 xRadius += scaledStroke.fX;
1664 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001665 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001666 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001667
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001668 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001669
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001670 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001671 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001672
joshualitt76e7fb62015-02-11 08:52:27 -08001673 RRectEllipseRendererBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -08001674 geometry.fColor = color;
1675 geometry.fXRadius = xRadius;
1676 geometry.fYRadius = yRadius;
1677 geometry.fInnerXRadius = innerXRadius;
1678 geometry.fInnerYRadius = innerYRadius;
joshualittd96a67b2015-05-05 14:09:05 -07001679 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001680
bsalomoncdaa97b2016-03-08 08:30:14 -08001681 return new RRectEllipseRendererBatch(geometry, viewMatrix, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001682 }
joshualitt3e708c52015-04-30 13:49:27 -07001683}
1684
robertphillipsb56f9272016-02-25 11:03:52 -08001685GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001686 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001687 const SkRRect& rrect,
1688 const SkStrokeRec& stroke,
1689 GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001690 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001691 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001692 }
1693
1694 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001695 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001696 }
1697
robertphillips0cc2f852016-02-24 13:36:56 -08001698 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001699}
joshualitt3e708c52015-04-30 13:49:27 -07001700
1701///////////////////////////////////////////////////////////////////////////////////////////////////
1702
1703#ifdef GR_TEST_UTILS
1704
bsalomonabd30f52015-08-13 13:34:48 -07001705DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001706 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1707 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001708 SkRect circle = GrTest::TestSquare(random);
robertphillips0cc2f852016-02-24 13:36:56 -08001709 return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001710}
1711
bsalomonabd30f52015-08-13 13:34:48 -07001712DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001713 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1714 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001715 SkRect ellipse = GrTest::TestSquare(random);
robertphillips0cc2f852016-02-24 13:36:56 -08001716 return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001717}
1718
bsalomonabd30f52015-08-13 13:34:48 -07001719DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001720 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1721 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001722 SkRect ellipse = GrTest::TestSquare(random);
robertphillips0cc2f852016-02-24 13:36:56 -08001723 return create_diellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001724}
1725
bsalomonabd30f52015-08-13 13:34:48 -07001726DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001727 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1728 GrColor color = GrRandomColor(random);
1729 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07001730 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001731}
1732
1733#endif