blob: cef08f431e3a4f047737d6d845c66e58b8f2cf3a [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
bungeman06ca8ec2016-06-09 08:01:03 -0700187sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
188 return sk_sp<GrGeometryProcessor>(
189 new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194/**
195 * 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 +0000196 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
197 * in both x and y directions.
198 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000199 * 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 +0000200 */
201
bsalomoncdaa97b2016-03-08 08:30:14 -0800202class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800204 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
205 : fLocalMatrix(localMatrix) {
206 this->initClassID<EllipseGeometryProcessor>();
207 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
208 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
209 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
210 kVec2f_GrVertexAttribType));
211 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
212 kVec4f_GrVertexAttribType));
213 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214 }
215
bsalomoncdaa97b2016-03-08 08:30:14 -0800216 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217
mtklein36352bf2015-03-25 18:17:31 -0700218 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800219
joshualitt71c92602015-01-14 08:12:47 -0800220 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800221 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800222 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
223 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitte3ababe2015-05-15 07:56:07 -0700224 const SkMatrix& localMatrix() const { return fLocalMatrix; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225
egdaniel57d3b032015-11-13 11:57:27 -0800226 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800228 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229
mtklein36352bf2015-03-25 18:17:31 -0700230 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800231 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800232 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800233 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800234 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235
joshualittabb52a12015-01-13 15:02:10 -0800236 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800237 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800238
egdaniel8dcdedc2015-11-11 06:27:20 -0800239 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800240 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800241 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800242 egp.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243
egdaniel8dcdedc2015-11-11 06:27:20 -0800244 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800245 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800246 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800247 egp.inEllipseRadii()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800248
cdalton85285412016-02-18 12:37:07 -0800249 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700250 // setup pass through color
bsalomoncdaa97b2016-03-08 08:30:14 -0800251 varyingHandler->addPassThroughAttribute(egp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800252
joshualittabb52a12015-01-13 15:02:10 -0800253 // Setup position
bsalomoncdaa97b2016-03-08 08:30:14 -0800254 this->setupPosition(vertBuilder, gpArgs, egp.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800255
256 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800257 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800258 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800259 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800260 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800261 egp.inPosition()->fName,
262 egp.localMatrix(),
egdaniel4ca2e602015-11-18 08:01:26 -0800263 args.fTransformsIn,
264 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800265
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000266 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800267 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
268 ellipseRadii.fsIn());
269 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
270 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
271 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700272
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000273 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800274 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
275 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800276 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000277
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000278 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800279 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800280 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
281 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
282 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
283 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
284 ellipseRadii.fsIn());
285 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
286 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000287 }
288
egdaniel4ca2e602015-11-18 08:01:26 -0800289 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290 }
291
robertphillips46d36f02015-01-18 08:14:14 -0800292 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700293 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700294 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800295 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
296 uint16_t key = egp.fStroke ? 0x1 : 0x0;
297 key |= egp.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700298 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000299 }
300
egdaniel018fb622015-10-28 07:26:40 -0700301 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000302 }
303
joshualitte3ababe2015-05-15 07:56:07 -0700304 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700305 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700306 int index,
307 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800308 this->setTransformDataHelper<EllipseGeometryProcessor>(primProc, pdman, index,
309 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700310 }
311
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000312 private:
egdaniele659a582015-11-13 09:55:43 -0800313 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314 };
315
egdaniel57d3b032015-11-13 11:57:27 -0800316 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
317 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800318 }
319
egdaniel57d3b032015-11-13 11:57:27 -0800320 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
321 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800322 }
323
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000324private:
joshualitt71c92602015-01-14 08:12:47 -0800325 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800326 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800327 const Attribute* fInEllipseOffset;
328 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700329 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000330 bool fStroke;
331
joshualittb0a8a372014-09-23 09:50:21 -0700332 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333
joshualitt249af152014-09-15 11:41:13 -0700334 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000335};
336
bsalomoncdaa97b2016-03-08 08:30:14 -0800337GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000338
bungeman06ca8ec2016-06-09 08:01:03 -0700339sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
340 return sk_sp<GrGeometryProcessor>(
341 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000342}
343
344///////////////////////////////////////////////////////////////////////////////
345
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000346/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000347 * 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 +0000348 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
349 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
350 * using differentials.
351 *
352 * The result is device-independent and can be used with any affine matrix.
353 */
354
bsalomoncdaa97b2016-03-08 08:30:14 -0800355enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000356
bsalomoncdaa97b2016-03-08 08:30:14 -0800357class DIEllipseGeometryProcessor : public GrGeometryProcessor {
358public:
359 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
360 : fViewMatrix(viewMatrix) {
361 this->initClassID<DIEllipseGeometryProcessor>();
362 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
363 kHigh_GrSLPrecision));
364 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
365 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
366 kVec2f_GrVertexAttribType));
367 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
368 kVec2f_GrVertexAttribType));
369 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370 }
371
bsalomoncdaa97b2016-03-08 08:30:14 -0800372
373 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000374
mtklein36352bf2015-03-25 18:17:31 -0700375 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000376
joshualitt71c92602015-01-14 08:12:47 -0800377 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800378 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800379 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
380 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitte578a952015-05-14 10:09:13 -0700381 const SkMatrix& viewMatrix() const { return fViewMatrix; }
halcanary9d524f22016-03-29 09:03:52 -0700382
egdaniel57d3b032015-11-13 11:57:27 -0800383 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000384 public:
egdaniel57d3b032015-11-13 11:57:27 -0800385 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800386 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000387
joshualitt465283c2015-09-11 08:19:35 -0700388 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800389 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800390 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800391 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800392 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000393
joshualittabb52a12015-01-13 15:02:10 -0800394 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800395 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800396
egdaniel8dcdedc2015-11-11 06:27:20 -0800397 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800398 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800399 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800400 diegp.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700401
egdaniel8dcdedc2015-11-11 06:27:20 -0800402 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800403 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800404 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800405 diegp.inEllipseOffsets1()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800406
cdalton85285412016-02-18 12:37:07 -0800407 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomoncdaa97b2016-03-08 08:30:14 -0800408 varyingHandler->addPassThroughAttribute(diegp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800409
joshualittabb52a12015-01-13 15:02:10 -0800410 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800411 this->setupPosition(vertBuilder,
412 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800413 gpArgs,
bsalomoncdaa97b2016-03-08 08:30:14 -0800414 diegp.inPosition()->fName,
415 diegp.viewMatrix(),
joshualitt5559ca22015-05-21 15:50:36 -0700416 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800417
418 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800419 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800420 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800421 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800422 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800423 diegp.inPosition()->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800424 args.fTransformsIn,
425 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800426
egdaniel4ca2e602015-11-18 08:01:26 -0800427 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800428 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000429 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800430 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
431 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
432 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
433 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
434 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
435 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800436 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
437 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000438
egdaniel4ca2e602015-11-18 08:01:26 -0800439 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000440 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800441 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
442 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800443 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800445 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
446 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000447 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800448 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 }
450
451 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800452 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800453 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
454 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
455 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
456 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
457 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
458 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
459 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
460 offsets1.fsIn());
461 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
462 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000463 }
464
egdaniel4ca2e602015-11-18 08:01:26 -0800465 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466 }
467
robertphillips46d36f02015-01-18 08:14:14 -0800468 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700469 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700470 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800471 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
472 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
473 key |= ComputePosKey(diegp.viewMatrix()) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700474 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000475 }
476
egdaniel018fb622015-10-28 07:26:40 -0700477 void setData(const GrGLSLProgramDataManager& pdman,
478 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800479 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700480
bsalomoncdaa97b2016-03-08 08:30:14 -0800481 if (!diegp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(diegp.viewMatrix())) {
482 fViewMatrix = diegp.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700483 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800484 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700485 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
486 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 }
488
489 private:
joshualitt5559ca22015-05-21 15:50:36 -0700490 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700491 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800492
egdaniele659a582015-11-13 09:55:43 -0800493 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000494 };
495
egdaniel57d3b032015-11-13 11:57:27 -0800496 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
497 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800498 }
499
egdaniel57d3b032015-11-13 11:57:27 -0800500 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
501 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800502 }
503
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000504private:
joshualitt71c92602015-01-14 08:12:47 -0800505 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800506 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800507 const Attribute* fInEllipseOffsets0;
508 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800509 SkMatrix fViewMatrix;
510 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000511
joshualittb0a8a372014-09-23 09:50:21 -0700512 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513
joshualitt249af152014-09-15 11:41:13 -0700514 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000515};
516
bsalomoncdaa97b2016-03-08 08:30:14 -0800517GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518
bungeman06ca8ec2016-06-09 08:01:03 -0700519sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
520 return sk_sp<GrGeometryProcessor>(
521 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
522 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000523}
524
525///////////////////////////////////////////////////////////////////////////////
526
robertphillipsb56f9272016-02-25 11:03:52 -0800527GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -0800528 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -0800529 const SkRect& oval,
530 const SkStrokeRec& stroke,
531 GrShaderCaps* shaderCaps) {
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000532 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800533 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillips0cc2f852016-02-24 13:36:56 -0800534 return CreateCircleBatch(color, viewMatrix, oval, stroke);
535 }
halcanary9d524f22016-03-29 09:03:52 -0700536
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000537 // if we have shader derivative support, render as device-independent
robertphillips0cc2f852016-02-24 13:36:56 -0800538 if (shaderCaps->shaderDerivativeSupport()) {
539 return CreateDIEllipseBatch(color, viewMatrix, oval, stroke);
540 }
halcanary9d524f22016-03-29 09:03:52 -0700541
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542 // otherwise axis-aligned ellipses only
robertphillips0cc2f852016-02-24 13:36:56 -0800543 if (viewMatrix.rectStaysRect()) {
544 return CreateEllipseBatch(color, viewMatrix, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000545 }
546
robertphillips0cc2f852016-02-24 13:36:56 -0800547 return nullptr;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000548}
549
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000550///////////////////////////////////////////////////////////////////////////////
551
bsalomonabd30f52015-08-13 13:34:48 -0700552class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800553public:
reed1b55a962015-09-17 20:16:13 -0700554 DEFINE_BATCH_CLASS_ID
555
joshualitt76e7fb62015-02-11 08:52:27 -0800556 struct Geometry {
reed1b55a962015-09-17 20:16:13 -0700557 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800558 SkScalar fInnerRadius;
559 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700560 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800561 };
562
bsalomoncdaa97b2016-03-08 08:30:14 -0800563 CircleBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
564 : INHERITED(ClassID())
565 , fStroked(stroked)
566 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
567 fGeoData.push_back(geometry);
568 this->setBounds(geometry.fDevBounds);
569 }
mtklein36352bf2015-03-25 18:17:31 -0700570 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800571
robertphillipse004bfc2015-11-16 09:06:59 -0800572 SkString dumpInfo() const override {
573 SkString string;
574 for (int i = 0; i < fGeoData.count(); ++i) {
575 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
576 "InnerRad: %.2f, OuterRad: %.2f\n",
577 fGeoData[i].fColor,
578 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
579 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
580 fGeoData[i].fInnerRadius,
581 fGeoData[i].fOuterRadius);
582 }
583 string.append(INHERITED::dumpInfo());
584 return string;
585 }
586
halcanary9d524f22016-03-29 09:03:52 -0700587 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800588 GrInitInvariantOutput* coverage,
589 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800590 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800591 color->setKnownFourComponents(fGeoData[0].fColor);
592 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800593 }
594
bsalomone46f9fe2015-08-18 06:05:14 -0700595private:
ethannicholasff210322015-11-24 12:10:10 -0800596 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800597 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800598 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800599 if (!overrides.readsLocalCoords()) {
600 fViewMatrixIfUsingLocalCoords.reset();
601 }
joshualitt76e7fb62015-02-11 08:52:27 -0800602 }
603
joshualitt144c3c82015-11-30 12:30:13 -0800604 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800605 SkMatrix localMatrix;
606 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800607 return;
608 }
609
610 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800611 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800612
joshualitt76e7fb62015-02-11 08:52:27 -0800613 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800614 size_t vertexStride = gp->getVertexStride();
615 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700616 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700617 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700618 instanceCount));
619 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800620 return;
621 }
622
joshualitt76e7fb62015-02-11 08:52:27 -0800623 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800624 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800625
brianosmanbb2ff942016-02-11 14:15:18 -0800626 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700627 SkScalar innerRadius = geom.fInnerRadius;
628 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800629
bsalomonb5238a72015-05-05 07:49:49 -0700630 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800631
632 // The inner radius in the vertex data must be specified in normalized space.
633 innerRadius = innerRadius / outerRadius;
634 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800635 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800636 verts[0].fOffset = SkPoint::Make(-1, -1);
637 verts[0].fOuterRadius = outerRadius;
638 verts[0].fInnerRadius = innerRadius;
639
640 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800641 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800642 verts[1].fOffset = SkPoint::Make(-1, 1);
643 verts[1].fOuterRadius = outerRadius;
644 verts[1].fInnerRadius = innerRadius;
645
646 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800647 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800648 verts[2].fOffset = SkPoint::Make(1, 1);
649 verts[2].fOuterRadius = outerRadius;
650 verts[2].fInnerRadius = innerRadius;
651
652 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800653 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800654 verts[3].fOffset = SkPoint::Make(1, -1);
655 verts[3].fOuterRadius = outerRadius;
656 verts[3].fInnerRadius = innerRadius;
657
bsalomonb5238a72015-05-05 07:49:49 -0700658 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800659 }
bsalomon342bfc22016-04-01 06:06:20 -0700660 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800661 }
662
bsalomoncb02b382015-08-12 11:14:50 -0700663 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700664 CircleBatch* that = t->cast<CircleBatch>();
665 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
666 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700667 return false;
668 }
669
bsalomoncdaa97b2016-03-08 08:30:14 -0800670 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800671 return false;
672 }
673
bsalomoncdaa97b2016-03-08 08:30:14 -0800674 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800675 return false;
676 }
677
bsalomoncdaa97b2016-03-08 08:30:14 -0800678 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -0700679 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800680 return true;
681 }
682
bsalomoncdaa97b2016-03-08 08:30:14 -0800683 bool fStroked;
684 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800685 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700686
687 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800688};
689
bsalomonabd30f52015-08-13 13:34:48 -0700690static GrDrawBatch* create_circle_batch(GrColor color,
691 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -0700692 const SkRect& circle,
693 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000694 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800695 viewMatrix.mapPoints(&center, 1);
696 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
697 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000698
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000699 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000700 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
701 SkStrokeRec::kHairline_Style == style;
702 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000703
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000704 SkScalar innerRadius = 0.0f;
705 SkScalar outerRadius = radius;
706 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000707 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000708 if (SkScalarNearlyZero(strokeWidth)) {
709 halfWidth = SK_ScalarHalf;
710 } else {
711 halfWidth = SkScalarHalf(strokeWidth);
712 }
713
714 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000715 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000716 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000717 }
718 }
719
bsalomonce1c8862014-12-15 07:11:22 -0800720 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
721 // computation because the computed alpha is zero, rather than 50%, at the radius.
722 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
723 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000724 outerRadius += SK_ScalarHalf;
725 innerRadius -= SK_ScalarHalf;
726
joshualitt76e7fb62015-02-11 08:52:27 -0800727 CircleBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -0800728 geometry.fColor = color;
729 geometry.fInnerRadius = innerRadius;
730 geometry.fOuterRadius = outerRadius;
joshualittd96a67b2015-05-05 14:09:05 -0700731 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
732 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000733
bsalomoncdaa97b2016-03-08 08:30:14 -0800734 return new CircleBatch(geometry, viewMatrix, isStrokeOnly && innerRadius > 0);
joshualitt3e708c52015-04-30 13:49:27 -0700735}
736
robertphillips0cc2f852016-02-24 13:36:56 -0800737GrDrawBatch* GrOvalRenderer::CreateCircleBatch(GrColor color,
738 const SkMatrix& viewMatrix,
739 const SkRect& circle,
740 const SkStrokeRec& stroke) {
741 return create_circle_batch(color, viewMatrix, circle, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000742}
743
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000744///////////////////////////////////////////////////////////////////////////////
745
bsalomonabd30f52015-08-13 13:34:48 -0700746class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800747public:
reed1b55a962015-09-17 20:16:13 -0700748 DEFINE_BATCH_CLASS_ID
749
joshualitt76e7fb62015-02-11 08:52:27 -0800750 struct Geometry {
reed1b55a962015-09-17 20:16:13 -0700751 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800752 SkScalar fXRadius;
753 SkScalar fYRadius;
754 SkScalar fInnerXRadius;
755 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700756 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800757 };
758
bsalomoncdaa97b2016-03-08 08:30:14 -0800759 EllipseBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
760 : INHERITED(ClassID())
761 , fStroked(stroked)
762 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
763 fGeoData.push_back(geometry);
764 this->setBounds(geometry.fDevBounds);
765 }
joshualitt76e7fb62015-02-11 08:52:27 -0800766
mtklein36352bf2015-03-25 18:17:31 -0700767 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800768
halcanary9d524f22016-03-29 09:03:52 -0700769 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800770 GrInitInvariantOutput* coverage,
771 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800772 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800773 color->setKnownFourComponents(fGeoData[0].fColor);
774 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800775 }
776
bsalomone46f9fe2015-08-18 06:05:14 -0700777private:
ethannicholasff210322015-11-24 12:10:10 -0800778 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800779 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800780 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800781 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800782 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800783 if (!overrides.readsLocalCoords()) {
784 fViewMatrixIfUsingLocalCoords.reset();
785 }
joshualitt76e7fb62015-02-11 08:52:27 -0800786 }
787
joshualitt144c3c82015-11-30 12:30:13 -0800788 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800789 SkMatrix localMatrix;
790 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800791 return;
792 }
793
794 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800795 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800796
joshualitt76e7fb62015-02-11 08:52:27 -0800797 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700798 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800799 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800800 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700801 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700802 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700803 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800804 return;
805 }
806
bsalomon8415abe2015-05-04 11:41:41 -0700807 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800808 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700809
brianosmanbb2ff942016-02-11 14:15:18 -0800810 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700811 SkScalar xRadius = geom.fXRadius;
812 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800813
814 // Compute the reciprocals of the radii here to save time in the shader
815 SkScalar xRadRecip = SkScalarInvert(xRadius);
816 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700817 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
818 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800819
bsalomonb5238a72015-05-05 07:49:49 -0700820 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800821
822 // The inner radius in the vertex data must be specified in normalized space.
823 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800824 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800825 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
826 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
827 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
828
829 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800830 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800831 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
832 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
833 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
834
835 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800836 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800837 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
838 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
839 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
840
841 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800842 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800843 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
844 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
845 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
846
bsalomonb5238a72015-05-05 07:49:49 -0700847 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800848 }
bsalomon342bfc22016-04-01 06:06:20 -0700849 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800850 }
851
bsalomoncb02b382015-08-12 11:14:50 -0700852 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700853 EllipseBatch* that = t->cast<EllipseBatch>();
854
855 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
856 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700857 return false;
858 }
859
bsalomoncdaa97b2016-03-08 08:30:14 -0800860 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800861 return false;
862 }
863
bsalomoncdaa97b2016-03-08 08:30:14 -0800864 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800865 return false;
866 }
867
bsalomoncdaa97b2016-03-08 08:30:14 -0800868 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -0700869 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800870 return true;
871 }
872
joshualitt76e7fb62015-02-11 08:52:27 -0800873
bsalomoncdaa97b2016-03-08 08:30:14 -0800874 bool fStroked;
875 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800876 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700877
878 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800879};
880
bsalomonabd30f52015-08-13 13:34:48 -0700881static GrDrawBatch* create_ellipse_batch(GrColor color,
882 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -0700883 const SkRect& ellipse,
884 const SkStrokeRec& stroke) {
robertphillips0cc2f852016-02-24 13:36:56 -0800885 SkASSERT(viewMatrix.rectStaysRect());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000886
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000887 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000888 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800889 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000890 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
891 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -0800892 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
893 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
894 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
895 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000896
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000897 // do (potentially) anisotropic mapping of stroke
898 SkVector scaledStroke;
899 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -0800900 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
901 viewMatrix[SkMatrix::kMSkewY]));
902 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
903 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000904
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000905 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000906 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
907 SkStrokeRec::kHairline_Style == style;
908 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000909
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000910 SkScalar innerXRadius = 0;
911 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000912 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000913 if (SkScalarNearlyZero(scaledStroke.length())) {
914 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
915 } else {
916 scaledStroke.scale(SK_ScalarHalf);
917 }
918
919 // we only handle thick strokes for near-circular ellipses
920 if (scaledStroke.length() > SK_ScalarHalf &&
921 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700922 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000923 }
924
925 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
926 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
927 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -0700928 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000929 }
930
931 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000932 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000933 innerXRadius = xRadius - scaledStroke.fX;
934 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000935 }
936
937 xRadius += scaledStroke.fX;
938 yRadius += scaledStroke.fY;
939 }
940
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000941 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000942 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000943 // TODO: Consider if we should use sqrt(2)/2 instead
brianosmanbd6366a2016-02-14 10:33:03 -0800944 xRadius += SK_ScalarHalf;
945 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000946
joshualitt76e7fb62015-02-11 08:52:27 -0800947 EllipseBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -0800948 geometry.fColor = color;
949 geometry.fXRadius = xRadius;
950 geometry.fYRadius = yRadius;
951 geometry.fInnerXRadius = innerXRadius;
952 geometry.fInnerYRadius = innerYRadius;
brianosmanbd6366a2016-02-14 10:33:03 -0800953 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
954 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000955
bsalomoncdaa97b2016-03-08 08:30:14 -0800956 return new EllipseBatch(geometry, viewMatrix,
957 isStrokeOnly && innerXRadius > 0 && innerYRadius > 0);
joshualitt3e708c52015-04-30 13:49:27 -0700958}
959
robertphillips0cc2f852016-02-24 13:36:56 -0800960GrDrawBatch* GrOvalRenderer::CreateEllipseBatch(GrColor color,
961 const SkMatrix& viewMatrix,
962 const SkRect& ellipse,
963 const SkStrokeRec& stroke) {
964 return create_ellipse_batch(color, viewMatrix, ellipse, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000965}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000966
joshualitt76e7fb62015-02-11 08:52:27 -0800967/////////////////////////////////////////////////////////////////////////////////////////////////
968
bsalomonabd30f52015-08-13 13:34:48 -0700969class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800970public:
reed1b55a962015-09-17 20:16:13 -0700971 DEFINE_BATCH_CLASS_ID
972
joshualitt76e7fb62015-02-11 08:52:27 -0800973 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800974 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700975 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800976 SkScalar fXRadius;
977 SkScalar fYRadius;
978 SkScalar fInnerXRadius;
979 SkScalar fInnerYRadius;
980 SkScalar fGeoDx;
981 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -0700982 GrColor fColor;
bsalomoncdaa97b2016-03-08 08:30:14 -0800983 DIEllipseStyle fStyle;
joshualitt76e7fb62015-02-11 08:52:27 -0800984 };
985
bsalomonabd30f52015-08-13 13:34:48 -0700986 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -0700987 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800988 }
989
mtklein36352bf2015-03-25 18:17:31 -0700990 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800991
halcanary9d524f22016-03-29 09:03:52 -0700992 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800993 GrInitInvariantOutput* coverage,
994 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800995 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800996 color->setKnownFourComponents(fGeoData[0].fColor);
997 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800998 }
999
bsalomone46f9fe2015-08-18 06:05:14 -07001000private:
1001
ethannicholasff210322015-11-24 12:10:10 -08001002 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001003 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001004 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001005 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001006 }
1007
joshualitt144c3c82015-11-30 12:30:13 -08001008 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001009 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001010 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1011 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001012
joshualitt76e7fb62015-02-11 08:52:27 -08001013 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001014 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001015 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001016 QuadHelper helper;
1017 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001018 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001019 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001020 return;
1021 }
1022
joshualitt76e7fb62015-02-11 08:52:27 -08001023 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001024 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001025
brianosmanbb2ff942016-02-11 14:15:18 -08001026 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001027 SkScalar xRadius = geom.fXRadius;
1028 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001029
bsalomonb5238a72015-05-05 07:49:49 -07001030 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001031
1032 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001033 SkScalar offsetDx = geom.fGeoDx / xRadius;
1034 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001035
reed80ea19c2015-05-12 10:37:34 -07001036 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1037 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001038
1039 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001040 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001041 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1042 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1043
1044 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001045 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001046 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1047 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1048
1049 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001050 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001051 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1052 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1053
1054 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001055 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001056 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1057 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1058
bsalomonb5238a72015-05-05 07:49:49 -07001059 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001060 }
bsalomon342bfc22016-04-01 06:06:20 -07001061 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001062 }
halcanary9d524f22016-03-29 09:03:52 -07001063
reed1b55a962015-09-17 20:16:13 -07001064 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001065 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001066
1067 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001068 }
1069
bsalomoncb02b382015-08-12 11:14:50 -07001070 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001071 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1072 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1073 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001074 return false;
1075 }
1076
bsalomoncdaa97b2016-03-08 08:30:14 -08001077 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001078 return false;
1079 }
1080
joshualittd96a67b2015-05-05 14:09:05 -07001081 // TODO rewrite to allow positioning on CPU
1082 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001083 return false;
1084 }
1085
bsalomoncdaa97b2016-03-08 08:30:14 -08001086 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001087 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001088 return true;
1089 }
1090
joshualitt76e7fb62015-02-11 08:52:27 -08001091 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001092 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001093
bsalomoncdaa97b2016-03-08 08:30:14 -08001094 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001095 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001096
1097 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001098};
1099
bsalomonabd30f52015-08-13 13:34:48 -07001100static GrDrawBatch* create_diellipse_batch(GrColor color,
1101 const SkMatrix& viewMatrix,
bsalomonabd30f52015-08-13 13:34:48 -07001102 const SkRect& ellipse,
1103 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001104 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001105 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001106 SkScalar yRadius = SkScalarHalf(ellipse.height());
1107
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001108 SkStrokeRec::Style style = stroke.getStyle();
bsalomoncdaa97b2016-03-08 08:30:14 -08001109 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1110 DIEllipseStyle::kStroke :
1111 (SkStrokeRec::kHairline_Style == style) ?
1112 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001113
1114 SkScalar innerXRadius = 0;
1115 SkScalar innerYRadius = 0;
1116 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1117 SkScalar strokeWidth = stroke.getWidth();
1118
1119 if (SkScalarNearlyZero(strokeWidth)) {
1120 strokeWidth = SK_ScalarHalf;
1121 } else {
1122 strokeWidth *= SK_ScalarHalf;
1123 }
1124
1125 // we only handle thick strokes for near-circular ellipses
1126 if (strokeWidth > SK_ScalarHalf &&
1127 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001128 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001129 }
1130
1131 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1132 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1133 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001134 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001135 }
1136
1137 // set inner radius (if needed)
1138 if (SkStrokeRec::kStroke_Style == style) {
1139 innerXRadius = xRadius - strokeWidth;
1140 innerYRadius = yRadius - strokeWidth;
1141 }
1142
1143 xRadius += strokeWidth;
1144 yRadius += strokeWidth;
1145 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001146 if (DIEllipseStyle::kStroke == dieStyle) {
1147 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1148 DIEllipseStyle ::kFill;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001149 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001150
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001151 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001152 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1153 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1154 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1155 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001156 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1157 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001158
joshualitt76e7fb62015-02-11 08:52:27 -08001159 DIEllipseBatch::Geometry geometry;
1160 geometry.fViewMatrix = viewMatrix;
1161 geometry.fColor = color;
1162 geometry.fXRadius = xRadius;
1163 geometry.fYRadius = yRadius;
1164 geometry.fInnerXRadius = innerXRadius;
1165 geometry.fInnerYRadius = innerYRadius;
1166 geometry.fGeoDx = geoDx;
1167 geometry.fGeoDy = geoDy;
bsalomoncdaa97b2016-03-08 08:30:14 -08001168 geometry.fStyle = dieStyle;
joshualittd96a67b2015-05-05 14:09:05 -07001169 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1170 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001171
joshualittd96a67b2015-05-05 14:09:05 -07001172 SkRect devBounds = geometry.fBounds;
1173 viewMatrix.mapRect(&devBounds);
1174 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001175}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001176
robertphillips0cc2f852016-02-24 13:36:56 -08001177GrDrawBatch* GrOvalRenderer::CreateDIEllipseBatch(GrColor color,
1178 const SkMatrix& viewMatrix,
1179 const SkRect& ellipse,
1180 const SkStrokeRec& stroke) {
1181 return create_diellipse_batch(color, viewMatrix, ellipse, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001182}
1183
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001184///////////////////////////////////////////////////////////////////////////////
1185
1186static const uint16_t gRRectIndices[] = {
1187 // corners
1188 0, 1, 5, 0, 5, 4,
1189 2, 3, 7, 2, 7, 6,
1190 8, 9, 13, 8, 13, 12,
1191 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001192
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001193 // edges
1194 1, 2, 6, 1, 6, 5,
1195 4, 5, 9, 4, 9, 8,
1196 6, 7, 11, 6, 11, 10,
1197 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001198
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001199 // center
1200 // we place this at the end so that we can ignore these indices when rendering stroke-only
1201 5, 6, 10, 5, 10, 9
1202};
1203
joshualitt5ead6da2014-10-22 16:00:29 -07001204static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1205static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1206static const int kVertsPerRRect = 16;
1207static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001208
bsalomoned0bcad2015-05-04 10:36:42 -07001209GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1210GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
cdalton397536c2016-03-25 12:15:03 -07001211static const GrBuffer* ref_rrect_index_buffer(bool strokeOnly,
1212 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001213 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1214 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1215 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001216 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001217 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1218 gStrokeRRectOnlyIndexBufferKey);
1219 } else {
bsalomoneae62002015-07-31 13:59:30 -07001220 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001221 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1222 gRRectOnlyIndexBufferKey);
1223
1224 }
1225}
1226
joshualitt76e7fb62015-02-11 08:52:27 -08001227///////////////////////////////////////////////////////////////////////////////////////////////////
1228
bsalomonabd30f52015-08-13 13:34:48 -07001229class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001230public:
reed1b55a962015-09-17 20:16:13 -07001231 DEFINE_BATCH_CLASS_ID
1232
joshualitt76e7fb62015-02-11 08:52:27 -08001233 struct Geometry {
reed1b55a962015-09-17 20:16:13 -07001234 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001235 SkScalar fInnerRadius;
1236 SkScalar fOuterRadius;
bsalomoncdaa97b2016-03-08 08:30:14 -08001237 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001238 };
1239
bsalomoncdaa97b2016-03-08 08:30:14 -08001240 RRectCircleRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
1241 : INHERITED(ClassID())
1242 , fStroked(stroked)
1243 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1244 fGeoData.push_back(geometry);
1245
1246 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001247 }
1248
mtklein36352bf2015-03-25 18:17:31 -07001249 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001250
halcanary9d524f22016-03-29 09:03:52 -07001251 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001252 GrInitInvariantOutput* coverage,
1253 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001254 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001255 color->setKnownFourComponents(fGeoData[0].fColor);
1256 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001257 }
1258
bsalomone46f9fe2015-08-18 06:05:14 -07001259private:
ethannicholasff210322015-11-24 12:10:10 -08001260 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001261 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001262 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001263 if (!overrides.readsLocalCoords()) {
1264 fViewMatrixIfUsingLocalCoords.reset();
1265 }
joshualitt76e7fb62015-02-11 08:52:27 -08001266 }
1267
joshualitt144c3c82015-11-30 12:30:13 -08001268 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001269 // Invert the view matrix as a local matrix (if any other processors require coords).
1270 SkMatrix localMatrix;
1271 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001272 return;
1273 }
1274
1275 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001276 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001277
joshualitt76e7fb62015-02-11 08:52:27 -08001278 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001279 size_t vertexStride = gp->getVertexStride();
1280 SkASSERT(vertexStride == sizeof(CircleVertex));
1281
bsalomonb5238a72015-05-05 07:49:49 -07001282 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001283 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001284 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001285 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001286
bsalomonb5238a72015-05-05 07:49:49 -07001287 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001288 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001289 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1290 indicesPerInstance, instanceCount));
1291 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001292 SkDebugf("Could not allocate vertices\n");
1293 return;
1294 }
1295
joshualitt76e7fb62015-02-11 08:52:27 -08001296 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001297 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001298
brianosmanbb2ff942016-02-11 14:15:18 -08001299 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001300 SkScalar outerRadius = args.fOuterRadius;
1301
egdanielbc227142015-04-21 06:28:08 -07001302 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001303
1304 SkScalar yCoords[4] = {
1305 bounds.fTop,
1306 bounds.fTop + outerRadius,
1307 bounds.fBottom - outerRadius,
1308 bounds.fBottom
1309 };
1310
1311 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1312 // The inner radius in the vertex data must be specified in normalized space.
1313 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1314 for (int i = 0; i < 4; ++i) {
1315 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001316 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001317 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1318 verts->fOuterRadius = outerRadius;
1319 verts->fInnerRadius = innerRadius;
1320 verts++;
1321
1322 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001323 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001324 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1325 verts->fOuterRadius = outerRadius;
1326 verts->fInnerRadius = innerRadius;
1327 verts++;
1328
1329 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001330 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001331 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1332 verts->fOuterRadius = outerRadius;
1333 verts->fInnerRadius = innerRadius;
1334 verts++;
1335
1336 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001337 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001338 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1339 verts->fOuterRadius = outerRadius;
1340 verts->fInnerRadius = innerRadius;
1341 verts++;
1342 }
1343 }
1344
bsalomon342bfc22016-04-01 06:06:20 -07001345 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001346 }
1347
bsalomoncb02b382015-08-12 11:14:50 -07001348 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001349 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1350 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1351 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001352 return false;
1353 }
1354
bsalomoncdaa97b2016-03-08 08:30:14 -08001355 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001356 return false;
1357 }
1358
bsalomoncdaa97b2016-03-08 08:30:14 -08001359 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001360 return false;
1361 }
1362
bsalomoncdaa97b2016-03-08 08:30:14 -08001363 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001364 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001365 return true;
1366 }
1367
bsalomoncdaa97b2016-03-08 08:30:14 -08001368 bool fStroked;
1369 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001370 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001371
1372 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001373};
1374
bsalomonabd30f52015-08-13 13:34:48 -07001375class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001376public:
reed1b55a962015-09-17 20:16:13 -07001377 DEFINE_BATCH_CLASS_ID
1378
joshualitt76e7fb62015-02-11 08:52:27 -08001379 struct Geometry {
reed1b55a962015-09-17 20:16:13 -07001380 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001381 SkScalar fXRadius;
1382 SkScalar fYRadius;
1383 SkScalar fInnerXRadius;
1384 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001385 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001386 };
1387
bsalomoncdaa97b2016-03-08 08:30:14 -08001388 RRectEllipseRendererBatch(const Geometry& geometry, const SkMatrix& viewMatrix, bool stroked)
1389 : INHERITED(ClassID())
1390 , fStroked(stroked)
1391 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1392 fGeoData.push_back(geometry);
1393 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001394 }
1395
mtklein36352bf2015-03-25 18:17:31 -07001396 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001397
halcanary9d524f22016-03-29 09:03:52 -07001398 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001399 GrInitInvariantOutput* coverage,
1400 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001401 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001402 color->setKnownFourComponents(fGeoData[0].fColor);
1403 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001404 }
1405
bsalomone46f9fe2015-08-18 06:05:14 -07001406private:
ethannicholasff210322015-11-24 12:10:10 -08001407 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001408 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001409 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001410 if (!overrides.readsLocalCoords()) {
1411 fViewMatrixIfUsingLocalCoords.reset();
1412 }
joshualitt76e7fb62015-02-11 08:52:27 -08001413 }
1414
joshualitt144c3c82015-11-30 12:30:13 -08001415 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001416 SkMatrix localMatrix;
1417 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001418 return;
1419 }
1420
1421 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001422 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001423
joshualitt76e7fb62015-02-11 08:52:27 -08001424 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001425 size_t vertexStride = gp->getVertexStride();
1426 SkASSERT(vertexStride == sizeof(EllipseVertex));
1427
bsalomonb5238a72015-05-05 07:49:49 -07001428 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001429 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001430 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001431 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001432
bsalomonb5238a72015-05-05 07:49:49 -07001433 InstancedHelper helper;
1434 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001435 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001436 kVertsPerRRect, indicesPerInstance, instanceCount));
1437 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001438 SkDebugf("Could not allocate vertices\n");
1439 return;
1440 }
1441
joshualitt76e7fb62015-02-11 08:52:27 -08001442 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001443 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001444
brianosmanbb2ff942016-02-11 14:15:18 -08001445 GrColor color = args.fColor;
1446
joshualitt76e7fb62015-02-11 08:52:27 -08001447 // Compute the reciprocals of the radii here to save time in the shader
1448 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1449 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1450 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1451 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1452
1453 // Extend the radii out half a pixel to antialias.
1454 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1455 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1456
egdanielbc227142015-04-21 06:28:08 -07001457 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001458
1459 SkScalar yCoords[4] = {
1460 bounds.fTop,
1461 bounds.fTop + yOuterRadius,
1462 bounds.fBottom - yOuterRadius,
1463 bounds.fBottom
1464 };
1465 SkScalar yOuterOffsets[4] = {
1466 yOuterRadius,
1467 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1468 SK_ScalarNearlyZero,
1469 yOuterRadius
1470 };
1471
1472 for (int i = 0; i < 4; ++i) {
1473 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001474 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001475 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1476 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1477 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1478 verts++;
1479
1480 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001481 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001482 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, 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.fRight - 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, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001495 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001496 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1497 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1498 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1499 verts++;
1500 }
1501 }
bsalomon342bfc22016-04-01 06:06:20 -07001502 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001503 }
1504
bsalomoncb02b382015-08-12 11:14:50 -07001505 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001506 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1507
1508 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1509 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001510 return false;
1511 }
1512
bsalomoncdaa97b2016-03-08 08:30:14 -08001513 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001514 return false;
1515 }
1516
bsalomoncdaa97b2016-03-08 08:30:14 -08001517 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001518 return false;
1519 }
1520
bsalomoncdaa97b2016-03-08 08:30:14 -08001521 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
joshualitt99c7c072015-05-01 13:43:30 -07001522 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001523 return true;
1524 }
1525
bsalomoncdaa97b2016-03-08 08:30:14 -08001526 bool fStroked;
1527 SkMatrix fViewMatrixIfUsingLocalCoords;
1528 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001529
1530 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001531};
1532
bsalomonabd30f52015-08-13 13:34:48 -07001533static GrDrawBatch* create_rrect_batch(GrColor color,
1534 const SkMatrix& viewMatrix,
1535 const SkRRect& rrect,
1536 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001537 SkASSERT(viewMatrix.rectStaysRect());
1538 SkASSERT(rrect.isSimple());
1539 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001540
joshualitt3e708c52015-04-30 13:49:27 -07001541 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001542 // do any matrix crunching before we reset the draw state for device coords
1543 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001544 SkRect bounds;
1545 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001546
1547 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001548 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1549 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1550 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1551 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001552
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001553 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001554
1555 // do (potentially) anisotropic mapping of stroke
1556 SkVector scaledStroke;
1557 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001558
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001559 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1560 SkStrokeRec::kHairline_Style == style;
1561 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1562
1563 if (hasStroke) {
1564 if (SkStrokeRec::kHairline_Style == style) {
1565 scaledStroke.set(1, 1);
1566 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001567 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1568 viewMatrix[SkMatrix::kMSkewY]));
1569 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1570 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001571 }
1572
1573 // if half of strokewidth is greater than radius, we don't handle that right now
1574 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001575 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001576 }
1577 }
1578
1579 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1580 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1581 // patch will have fractional coverage. This only matters when the interior is actually filled.
1582 // We could consider falling back to rect rendering here, since a tiny radius is
1583 // indistinguishable from a square corner.
1584 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001585 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001586 }
1587
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001588 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001589 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001590 SkScalar innerRadius = 0.0f;
1591 SkScalar outerRadius = xRadius;
1592 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001593 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001594 if (SkScalarNearlyZero(scaledStroke.fX)) {
1595 halfWidth = SK_ScalarHalf;
1596 } else {
1597 halfWidth = SkScalarHalf(scaledStroke.fX);
1598 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001599
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001600 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001601 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001602 }
1603 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001604 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001605 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001606
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001607 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001608
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001609 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001610 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1611 // Second, the outer radius is used to compute the verts of the bounding box that is
1612 // rendered and the outset ensures the box will cover all partially covered by the rrect
1613 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001614 outerRadius += SK_ScalarHalf;
1615 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001616
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001617 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001618 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001619
joshualitt76e7fb62015-02-11 08:52:27 -08001620 RRectCircleRendererBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -08001621 geometry.fColor = color;
1622 geometry.fInnerRadius = innerRadius;
1623 geometry.fOuterRadius = outerRadius;
joshualittd96a67b2015-05-05 14:09:05 -07001624 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001625
bsalomoncdaa97b2016-03-08 08:30:14 -08001626 return new RRectCircleRendererBatch(geometry, viewMatrix, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001627 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001628 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001629 SkScalar innerXRadius = 0.0f;
1630 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001631 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001632 if (SkScalarNearlyZero(scaledStroke.length())) {
1633 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1634 } else {
1635 scaledStroke.scale(SK_ScalarHalf);
1636 }
1637
1638 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001639 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001640 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001641 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001642 }
1643
1644 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1645 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1646 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001647 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001648 }
1649
1650 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001651 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001652 innerXRadius = xRadius - scaledStroke.fX;
1653 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001654 }
1655
1656 xRadius += scaledStroke.fX;
1657 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001658 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001659 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001660
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001661 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001662
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001663 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001664 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001665
joshualitt76e7fb62015-02-11 08:52:27 -08001666 RRectEllipseRendererBatch::Geometry geometry;
joshualitt76e7fb62015-02-11 08:52:27 -08001667 geometry.fColor = color;
1668 geometry.fXRadius = xRadius;
1669 geometry.fYRadius = yRadius;
1670 geometry.fInnerXRadius = innerXRadius;
1671 geometry.fInnerYRadius = innerYRadius;
joshualittd96a67b2015-05-05 14:09:05 -07001672 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001673
bsalomoncdaa97b2016-03-08 08:30:14 -08001674 return new RRectEllipseRendererBatch(geometry, viewMatrix, isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001675 }
joshualitt3e708c52015-04-30 13:49:27 -07001676}
1677
robertphillipsb56f9272016-02-25 11:03:52 -08001678GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001679 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001680 const SkRRect& rrect,
1681 const SkStrokeRec& stroke,
1682 GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001683 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001684 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001685 }
1686
1687 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001688 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001689 }
1690
robertphillips0cc2f852016-02-24 13:36:56 -08001691 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001692}
joshualitt3e708c52015-04-30 13:49:27 -07001693
1694///////////////////////////////////////////////////////////////////////////////////////////////////
1695
1696#ifdef GR_TEST_UTILS
1697
bsalomonabd30f52015-08-13 13:34:48 -07001698DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001699 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1700 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001701 SkRect circle = GrTest::TestSquare(random);
robertphillips0cc2f852016-02-24 13:36:56 -08001702 return create_circle_batch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001703}
1704
bsalomonabd30f52015-08-13 13:34:48 -07001705DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001706 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1707 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001708 SkRect ellipse = GrTest::TestSquare(random);
robertphillips0cc2f852016-02-24 13:36:56 -08001709 return create_ellipse_batch(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001710}
1711
bsalomonabd30f52015-08-13 13:34:48 -07001712DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001713 SkMatrix viewMatrix = GrTest::TestMatrix(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_diellipse_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(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001720 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1721 GrColor color = GrRandomColor(random);
1722 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07001723 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001724}
1725
1726#endif