blob: 0a7b3f89fce1f8c8976bbe1bb31bb32db1141ae2 [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
joshualitt76e7fb62015-02-11 08:52:27 -080010#include "GrBatch.h"
11#include "GrBatchTarget.h"
joshualitt3e708c52015-04-30 13:49:27 -070012#include "GrBatchTest.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000013#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080014#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080015#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080016#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080017#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070018#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070019#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000020#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000021#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000022#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080024#include "gl/GrGLProcessor.h"
25#include "gl/GrGLSL.h"
26#include "gl/GrGLGeometryProcessor.h"
27#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000028
joshualitt76e7fb62015-02-11 08:52:27 -080029// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
30
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070032// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000034 SkPoint fPos;
35 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000036 SkScalar fOuterRadius;
37 SkScalar fInnerRadius;
38};
39
40struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
42 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;
49 SkPoint fOuterOffset;
50 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000051};
52
commit-bot@chromium.org81312832013-03-22 18:34:09 +000053inline bool circle_stays_circle(const SkMatrix& m) {
54 return m.isSimilarity();
55}
56
57}
58
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000059///////////////////////////////////////////////////////////////////////////////
60
61/**
bsalomonce1c8862014-12-15 07:11:22 -080062 * The output of this effect is a modulation of the input color and coverage for a circle. It
63 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
64 * with origin at the circle center. Two vertex attributes are used:
65 * vec2f : position in device space of the bounding geometry vertices
66 * vec4f : (p.xy, outerRad, innerRad)
67 * p is the position in the normalized space.
68 * outerRad is the outerRadius in device space.
69 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070 */
71
joshualitt249af152014-09-15 11:41:13 -070072class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000073public:
joshualittd27f73e2014-12-29 07:43:36 -080074 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
75 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 }
77
joshualitt71c92602015-01-14 08:12:47 -080078 const Attribute* inPosition() const { return fInPosition; }
79 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070080 GrColor color() const { return fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070081 const SkMatrix& localMatrix() const { return fLocalMatrix; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000082 virtual ~CircleEdgeEffect() {}
83
mtklein36352bf2015-03-25 18:17:31 -070084 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000085
86 inline bool isStroked() const { return fStroke; }
87
joshualittb0a8a372014-09-23 09:50:21 -070088 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000089 public:
joshualitteb2a6762014-12-04 11:35:33 -080090 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080091 const GrBatchTracker&)
92 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000093
mtklein36352bf2015-03-25 18:17:31 -070094 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080095 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080096 GrGLGPBuilder* pb = args.fPB;
97 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080098 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
99
joshualittabb52a12015-01-13 15:02:10 -0800100 // emit attributes
101 vsBuilder->emitAttributes(ce);
102
joshualitt74077b92014-10-24 11:26:03 -0700103 GrGLVertToFrag v(kVec4f_GrSLType);
104 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800105 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000106
joshualitt9b989322014-12-15 14:16:27 -0800107 // Setup pass through color
108 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
109 &fColorUniform);
110
joshualittabb52a12015-01-13 15:02:10 -0800111 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700112 this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800113
114 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800115 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700116 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800117
egdaniel29bee0f2015-04-29 11:54:42 -0700118 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700119 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800120 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800121 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800122 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
123 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700124 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000125 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000126
joshualitt2dd1ae02014-12-03 06:24:10 -0800127 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000128 }
129
robertphillips46d36f02015-01-18 08:14:14 -0800130 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800131 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700132 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700133 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800134 const BatchTracker& local = bt.cast<BatchTracker>();
joshualitte3ababe2015-05-15 07:56:07 -0700135 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
136 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
137 key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800138 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000139 }
140
joshualitt9b989322014-12-15 14:16:27 -0800141 virtual void setData(const GrGLProgramDataManager& pdman,
142 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700143 const GrBatchTracker& bt) override {
joshualitt9b989322014-12-15 14:16:27 -0800144 const BatchTracker& local = bt.cast<BatchTracker>();
145 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
146 GrGLfloat c[4];
147 GrColorToRGBAFloat(local.fColor, c);
148 pdman.set4fv(fColorUniform, 1, c);
149 fColor = local.fColor;
150 }
151 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152
joshualitte3ababe2015-05-15 07:56:07 -0700153 void setTransformData(const GrPrimitiveProcessor& primProc,
154 const GrGLProgramDataManager& pdman,
155 int index,
156 const SkTArray<const GrCoordTransform*, true>& transforms) override {
157 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
158 }
159
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000160 private:
joshualitt9b989322014-12-15 14:16:27 -0800161 GrColor fColor;
162 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700163 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000164 };
165
joshualitteb2a6762014-12-04 11:35:33 -0800166 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700167 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700168 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800169 GLProcessor::GenKey(*this, bt, caps, b);
170 }
171
joshualittabb52a12015-01-13 15:02:10 -0800172 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700173 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800174 return SkNEW_ARGS(GLProcessor, (*this, bt));
175 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176
mtklein36352bf2015-03-25 18:17:31 -0700177 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800178 BatchTracker* local = bt->cast<BatchTracker>();
179 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800180 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800181 }
182
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000183private:
joshualittd27f73e2014-12-29 07:43:36 -0800184 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitte3ababe2015-05-15 07:56:07 -0700185 : fColor(color)
186 , fLocalMatrix(localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800187 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800188 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
189 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800190 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191 fStroke = stroke;
192 }
193
joshualitt9b989322014-12-15 14:16:27 -0800194 struct BatchTracker {
195 GrGPInput fInputColorType;
196 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800197 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800198 };
199
joshualitt88c23fc2015-05-13 14:18:07 -0700200 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700201 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800202 const Attribute* fInPosition;
203 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204 bool fStroke;
205
joshualittb0a8a372014-09-23 09:50:21 -0700206 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207
joshualitt249af152014-09-15 11:41:13 -0700208 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000209};
210
joshualittb0a8a372014-09-23 09:50:21 -0700211GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000212
joshualittb0a8a372014-09-23 09:50:21 -0700213GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
214 GrContext* context,
215 const GrDrawTargetCaps&,
216 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800217 return CircleEdgeEffect::Create(GrRandomColor(random),
218 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700219 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220}
221
222///////////////////////////////////////////////////////////////////////////////
223
224/**
225 * 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 +0000226 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
227 * in both x and y directions.
228 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000229 * 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 +0000230 */
231
joshualitt249af152014-09-15 11:41:13 -0700232class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233public:
joshualittd27f73e2014-12-29 07:43:36 -0800234 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
235 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000236 }
237
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238 virtual ~EllipseEdgeEffect() {}
239
mtklein36352bf2015-03-25 18:17:31 -0700240 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800241
joshualitt71c92602015-01-14 08:12:47 -0800242 const Attribute* inPosition() const { return fInPosition; }
243 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
244 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700245 GrColor color() const { return fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700246 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualitt249af152014-09-15 11:41:13 -0700247
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 inline bool isStroked() const { return fStroke; }
249
joshualittb0a8a372014-09-23 09:50:21 -0700250 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251 public:
joshualitteb2a6762014-12-04 11:35:33 -0800252 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800253 const GrBatchTracker&)
254 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000255
mtklein36352bf2015-03-25 18:17:31 -0700256 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800257 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800258 GrGLGPBuilder* pb = args.fPB;
259 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800260 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261
joshualittabb52a12015-01-13 15:02:10 -0800262 // emit attributes
263 vsBuilder->emitAttributes(ee);
264
joshualitt74077b92014-10-24 11:26:03 -0700265 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
266 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700267 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800268 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000269
joshualitt74077b92014-10-24 11:26:03 -0700270 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
271 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
272 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800273 ee.inEllipseRadii()->fName);
274
joshualitt9b989322014-12-15 14:16:27 -0800275 // Setup pass through color
276 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
277 &fColorUniform);
278
joshualittabb52a12015-01-13 15:02:10 -0800279 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700280 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800281
282 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800283 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800284 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800285
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000286 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700287 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700288 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
289 ellipseRadii.fsIn());
290 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
291 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
292 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
293
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000294 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700295 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
296 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
297 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000298
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000299 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800300 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700301 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
302 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
303 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
304 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
305 ellipseRadii.fsIn());
306 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
307 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000308 }
309
joshualitt2dd1ae02014-12-03 06:24:10 -0800310 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000311 }
312
robertphillips46d36f02015-01-18 08:14:14 -0800313 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800314 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700315 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700316 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800317 const BatchTracker& local = bt.cast<BatchTracker>();
joshualitte3ababe2015-05-15 07:56:07 -0700318 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
319 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
320 key |= local.fUsesLocalCoords && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800321 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000322 }
323
joshualitt9b989322014-12-15 14:16:27 -0800324 virtual void setData(const GrGLProgramDataManager& pdman,
325 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700326 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800327
joshualitt9b989322014-12-15 14:16:27 -0800328 const BatchTracker& local = bt.cast<BatchTracker>();
329 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
330 GrGLfloat c[4];
331 GrColorToRGBAFloat(local.fColor, c);
332 pdman.set4fv(fColorUniform, 1, c);
333 fColor = local.fColor;
334 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000335 }
336
joshualitte3ababe2015-05-15 07:56:07 -0700337 void setTransformData(const GrPrimitiveProcessor& primProc,
338 const GrGLProgramDataManager& pdman,
339 int index,
340 const SkTArray<const GrCoordTransform*, true>& transforms) override {
341 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
342 }
343
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000344 private:
joshualitt9b989322014-12-15 14:16:27 -0800345 GrColor fColor;
346 UniformHandle fColorUniform;
347
joshualitt249af152014-09-15 11:41:13 -0700348 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000349 };
350
joshualitteb2a6762014-12-04 11:35:33 -0800351 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700352 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700353 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800354 GLProcessor::GenKey(*this, bt, caps, b);
355 }
356
joshualittabb52a12015-01-13 15:02:10 -0800357 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700358 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800359 return SkNEW_ARGS(GLProcessor, (*this, bt));
360 }
361
mtklein36352bf2015-03-25 18:17:31 -0700362 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800363 BatchTracker* local = bt->cast<BatchTracker>();
364 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800365 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800366 }
367
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000368private:
joshualittd27f73e2014-12-29 07:43:36 -0800369 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitte3ababe2015-05-15 07:56:07 -0700370 : fColor(color)
371 , fLocalMatrix(localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800372 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800373 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
374 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800375 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800376 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800377 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000378 fStroke = stroke;
379 }
380
joshualitt9b989322014-12-15 14:16:27 -0800381 struct BatchTracker {
382 GrGPInput fInputColorType;
383 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800384 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800385 };
386
joshualitt71c92602015-01-14 08:12:47 -0800387 const Attribute* fInPosition;
388 const Attribute* fInEllipseOffset;
389 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700390 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700391 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392 bool fStroke;
393
joshualittb0a8a372014-09-23 09:50:21 -0700394 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000395
joshualitt249af152014-09-15 11:41:13 -0700396 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000397};
398
joshualittb0a8a372014-09-23 09:50:21 -0700399GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000400
joshualittb0a8a372014-09-23 09:50:21 -0700401GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
402 GrContext* context,
403 const GrDrawTargetCaps&,
404 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800405 return EllipseEdgeEffect::Create(GrRandomColor(random),
406 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700407 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000408}
409
410///////////////////////////////////////////////////////////////////////////////
411
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000413 * 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 +0000414 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
415 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
416 * using differentials.
417 *
418 * The result is device-independent and can be used with any affine matrix.
419 */
420
joshualitt249af152014-09-15 11:41:13 -0700421class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000422public:
423 enum Mode { kStroke = 0, kHairline, kFill };
424
joshualitt8059eb92014-12-29 15:10:07 -0800425 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
426 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427 }
428
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000429 virtual ~DIEllipseEdgeEffect() {}
430
mtklein36352bf2015-03-25 18:17:31 -0700431 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000432
joshualitt71c92602015-01-14 08:12:47 -0800433 const Attribute* inPosition() const { return fInPosition; }
434 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
435 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700436 GrColor color() const { return fColor; }
joshualitte578a952015-05-14 10:09:13 -0700437 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualitt249af152014-09-15 11:41:13 -0700438
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000439 inline Mode getMode() const { return fMode; }
440
joshualittb0a8a372014-09-23 09:50:21 -0700441 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000442 public:
joshualitteb2a6762014-12-04 11:35:33 -0800443 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800444 const GrBatchTracker&)
445 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000446
mtklein36352bf2015-03-25 18:17:31 -0700447 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800448 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800449 GrGLGPBuilder* pb = args.fPB;
450 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800451 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000452
joshualittabb52a12015-01-13 15:02:10 -0800453 // emit attributes
454 vsBuilder->emitAttributes(ee);
455
joshualitt74077b92014-10-24 11:26:03 -0700456 GrGLVertToFrag offsets0(kVec2f_GrSLType);
457 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700458 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800459 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700460
461 GrGLVertToFrag offsets1(kVec2f_GrSLType);
462 args.fPB->addVarying("EllipseOffsets1", &offsets1);
463 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800464 ee.inEllipseOffsets1()->fName);
465
joshualitt9b989322014-12-15 14:16:27 -0800466 // Setup pass through color
467 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
468 &fColorUniform);
469
joshualittabb52a12015-01-13 15:02:10 -0800470 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800471 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800472
473 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800474 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualitte3ababe2015-05-15 07:56:07 -0700475 args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800476
egdaniel29bee0f2015-04-29 11:54:42 -0700477 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700478 SkAssertResult(fsBuilder->enableFeature(
479 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000480 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700481 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
482 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
483 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
484 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
485 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
486 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
487 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488
joshualitt74077b92014-10-24 11:26:03 -0700489 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000490 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700491 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
492 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800493 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000494 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700495 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
496 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000497 } else {
joshualitt74077b92014-10-24 11:26:03 -0700498 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000499 }
500
501 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800502 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700503 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
504 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
505 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
506 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
507 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
508 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
509 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
510 offsets1.fsIn());
511 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
512 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 }
514
joshualitt2dd1ae02014-12-03 06:24:10 -0800515 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000516 }
517
robertphillips46d36f02015-01-18 08:14:14 -0800518 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800519 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700520 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700521 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800522 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800523 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800524 uint16_t key = ellipseEffect.getMode();
joshualitte578a952015-05-14 10:09:13 -0700525 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800526 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 }
528
joshualitt9b989322014-12-15 14:16:27 -0800529 virtual void setData(const GrGLProgramDataManager& pdman,
530 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700531 const GrBatchTracker& bt) override {
joshualitte578a952015-05-14 10:09:13 -0700532 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
533 this->setUniformViewMatrix(pdman, dee.viewMatrix());
joshualittee2af952014-12-30 09:04:15 -0800534
joshualitt9b989322014-12-15 14:16:27 -0800535 const BatchTracker& local = bt.cast<BatchTracker>();
536 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
537 GrGLfloat c[4];
538 GrColorToRGBAFloat(local.fColor, c);
539 pdman.set4fv(fColorUniform, 1, c);
540 fColor = local.fColor;
541 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542 }
543
544 private:
joshualitt9b989322014-12-15 14:16:27 -0800545 GrColor fColor;
546 UniformHandle fColorUniform;
547
joshualitt249af152014-09-15 11:41:13 -0700548 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000549 };
550
joshualitteb2a6762014-12-04 11:35:33 -0800551 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700552 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700553 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800554 GLProcessor::GenKey(*this, bt, caps, b);
555 }
556
joshualittabb52a12015-01-13 15:02:10 -0800557 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700558 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800559 return SkNEW_ARGS(GLProcessor, (*this, bt));
560 }
561
mtklein36352bf2015-03-25 18:17:31 -0700562 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800563 BatchTracker* local = bt->cast<BatchTracker>();
564 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800565 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800566 }
567
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000568private:
joshualitt8059eb92014-12-29 15:10:07 -0800569 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
joshualitte578a952015-05-14 10:09:13 -0700570 : fColor(color)
571 , fViewMatrix(viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800572 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800573 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
574 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800575 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800576 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800577 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000578 fMode = mode;
579 }
580
joshualitt9b989322014-12-15 14:16:27 -0800581 struct BatchTracker {
582 GrGPInput fInputColorType;
583 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800584 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800585 };
586
joshualitt71c92602015-01-14 08:12:47 -0800587 const Attribute* fInPosition;
588 const Attribute* fInEllipseOffsets0;
589 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700590 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700591 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000592 Mode fMode;
593
joshualittb0a8a372014-09-23 09:50:21 -0700594 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000595
joshualitt249af152014-09-15 11:41:13 -0700596 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000597};
598
joshualittb0a8a372014-09-23 09:50:21 -0700599GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000600
joshualittb0a8a372014-09-23 09:50:21 -0700601GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
602 GrContext* context,
603 const GrDrawTargetCaps&,
604 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800605 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700606 GrTest::TestMatrix(random),
joshualitt8059eb92014-12-29 15:10:07 -0800607 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000608}
609
610///////////////////////////////////////////////////////////////////////////////
611
joshualitt9853cce2014-11-17 14:22:48 -0800612bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800613 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800614 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800615 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800616 bool useAA,
617 const SkRect& oval,
618 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000619{
bsalomoned0bcad2015-05-04 10:36:42 -0700620 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000621
622 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000623 return false;
624 }
625
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000626 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800627 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800628 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000629 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700630 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800631 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800632 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000633 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800634 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800635 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
636 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000637 } else {
638 return false;
639 }
640
641 return true;
642}
643
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000644///////////////////////////////////////////////////////////////////////////////
645
joshualitt76e7fb62015-02-11 08:52:27 -0800646class CircleBatch : public GrBatch {
647public:
648 struct Geometry {
649 GrColor fColor;
650 SkMatrix fViewMatrix;
651 SkScalar fInnerRadius;
652 SkScalar fOuterRadius;
653 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700654 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800655 };
656
bsalomoned0bcad2015-05-04 10:36:42 -0700657 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
joshualitt76e7fb62015-02-11 08:52:27 -0800658
mtklein36352bf2015-03-25 18:17:31 -0700659 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800660
mtklein36352bf2015-03-25 18:17:31 -0700661 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800662 // When this is called on a batch, there is only one geometry bundle
663 out->setKnownFourComponents(fGeoData[0].fColor);
664 }
665
mtklein36352bf2015-03-25 18:17:31 -0700666 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800667 out->setUnknownSingleComponent();
668 }
669
mtklein36352bf2015-03-25 18:17:31 -0700670 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800671 // Handle any color overrides
672 if (init.fColorIgnored) {
673 fGeoData[0].fColor = GrColor_ILLEGAL;
674 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
675 fGeoData[0].fColor = init.fOverrideColor;
676 }
677
678 // setup batch properties
679 fBatch.fColorIgnored = init.fColorIgnored;
680 fBatch.fColor = fGeoData[0].fColor;
681 fBatch.fStroke = fGeoData[0].fStroke;
682 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
683 fBatch.fCoverageIgnored = init.fCoverageIgnored;
684 }
685
mtklein36352bf2015-03-25 18:17:31 -0700686 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800687 SkMatrix invert;
688 if (!this->viewMatrix().invert(&invert)) {
689 return;
690 }
691
692 // Setup geometry processor
693 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
694 this->stroke(),
695 invert));
696
697 batchTarget->initDraw(gp, pipeline);
698
699 // TODO this is hacky, but the only way we have to initialize the GP is to use the
700 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
701 // everywhere we can remove this nastiness
702 GrPipelineInfo init;
703 init.fColorIgnored = fBatch.fColorIgnored;
704 init.fOverrideColor = GrColor_ILLEGAL;
705 init.fCoverageIgnored = fBatch.fCoverageIgnored;
706 init.fUsesLocalCoords = this->usesLocalCoords();
707 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
708
709 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800710 size_t vertexStride = gp->getVertexStride();
711 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700712 QuadHelper helper;
713 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride,
714 instanceCount));
715 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800716 return;
717 }
718
joshualitt76e7fb62015-02-11 08:52:27 -0800719 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700720 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800721
bsalomonb5238a72015-05-05 07:49:49 -0700722 SkScalar innerRadius = geom.fInnerRadius;
723 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800724
bsalomonb5238a72015-05-05 07:49:49 -0700725 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800726
727 // The inner radius in the vertex data must be specified in normalized space.
728 innerRadius = innerRadius / outerRadius;
729 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
730 verts[0].fOffset = SkPoint::Make(-1, -1);
731 verts[0].fOuterRadius = outerRadius;
732 verts[0].fInnerRadius = innerRadius;
733
734 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
735 verts[1].fOffset = SkPoint::Make(-1, 1);
736 verts[1].fOuterRadius = outerRadius;
737 verts[1].fInnerRadius = innerRadius;
738
739 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
740 verts[2].fOffset = SkPoint::Make(1, 1);
741 verts[2].fOuterRadius = outerRadius;
742 verts[2].fInnerRadius = innerRadius;
743
744 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
745 verts[3].fOffset = SkPoint::Make(1, -1);
746 verts[3].fOuterRadius = outerRadius;
747 verts[3].fInnerRadius = innerRadius;
748
bsalomonb5238a72015-05-05 07:49:49 -0700749 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800750 }
bsalomone64eb572015-05-07 11:35:55 -0700751 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -0800752 }
753
754 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
755
756private:
757 CircleBatch(const Geometry& geometry) {
758 this->initClassID<CircleBatch>();
759 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700760
761 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800762 }
763
mtklein36352bf2015-03-25 18:17:31 -0700764 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800765 CircleBatch* that = t->cast<CircleBatch>();
766
767 // TODO use vertex color to avoid breaking batches
768 if (this->color() != that->color()) {
769 return false;
770 }
771
772 if (this->stroke() != that->stroke()) {
773 return false;
774 }
775
776 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
777 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
778 return false;
779 }
780
781 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700782 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800783 return true;
784 }
785
786 GrColor color() const { return fBatch.fColor; }
787 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
788 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
789 bool stroke() const { return fBatch.fStroke; }
790
791 struct BatchTracker {
792 GrColor fColor;
793 bool fStroke;
794 bool fUsesLocalCoords;
795 bool fColorIgnored;
796 bool fCoverageIgnored;
797 };
798
joshualitt76e7fb62015-02-11 08:52:27 -0800799 BatchTracker fBatch;
800 SkSTArray<1, Geometry, true> fGeoData;
801};
802
joshualitt3e708c52015-04-30 13:49:27 -0700803static GrBatch* create_circle_batch(GrColor color,
804 const SkMatrix& viewMatrix,
805 bool useCoverageAA,
806 const SkRect& circle,
joshualittd96a67b2015-05-05 14:09:05 -0700807 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000808 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800809 viewMatrix.mapPoints(&center, 1);
810 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
811 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000812
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000813 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000814 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
815 SkStrokeRec::kHairline_Style == style;
816 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000817
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000818 SkScalar innerRadius = 0.0f;
819 SkScalar outerRadius = radius;
820 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000821 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000822 if (SkScalarNearlyZero(strokeWidth)) {
823 halfWidth = SK_ScalarHalf;
824 } else {
825 halfWidth = SkScalarHalf(strokeWidth);
826 }
827
828 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000829 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000830 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000831 }
832 }
833
bsalomonce1c8862014-12-15 07:11:22 -0800834 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
835 // computation because the computed alpha is zero, rather than 50%, at the radius.
836 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
837 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000838 outerRadius += SK_ScalarHalf;
839 innerRadius -= SK_ScalarHalf;
840
joshualitt76e7fb62015-02-11 08:52:27 -0800841 CircleBatch::Geometry geometry;
842 geometry.fViewMatrix = viewMatrix;
843 geometry.fColor = color;
844 geometry.fInnerRadius = innerRadius;
845 geometry.fOuterRadius = outerRadius;
846 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700847 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
848 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000849
joshualitt3e708c52015-04-30 13:49:27 -0700850 return CircleBatch::Create(geometry);
851}
852
853void GrOvalRenderer::drawCircle(GrDrawTarget* target,
854 GrPipelineBuilder* pipelineBuilder,
855 GrColor color,
856 const SkMatrix& viewMatrix,
857 bool useCoverageAA,
858 const SkRect& circle,
859 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -0700860 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
joshualittd96a67b2015-05-05 14:09:05 -0700861 stroke));
joshualitt99c7c072015-05-01 13:43:30 -0700862 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000863}
864
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000865///////////////////////////////////////////////////////////////////////////////
866
joshualitt76e7fb62015-02-11 08:52:27 -0800867class EllipseBatch : public GrBatch {
868public:
869 struct Geometry {
870 GrColor fColor;
871 SkMatrix fViewMatrix;
872 SkScalar fXRadius;
873 SkScalar fYRadius;
874 SkScalar fInnerXRadius;
875 SkScalar fInnerYRadius;
876 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700877 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800878 };
879
880 static GrBatch* Create(const Geometry& geometry) {
881 return SkNEW_ARGS(EllipseBatch, (geometry));
882 }
883
mtklein36352bf2015-03-25 18:17:31 -0700884 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800885
mtklein36352bf2015-03-25 18:17:31 -0700886 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800887 // When this is called on a batch, there is only one geometry bundle
888 out->setKnownFourComponents(fGeoData[0].fColor);
889 }
mtklein36352bf2015-03-25 18:17:31 -0700890 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800891 out->setUnknownSingleComponent();
892 }
893
mtklein36352bf2015-03-25 18:17:31 -0700894 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800895 // Handle any color overrides
896 if (init.fColorIgnored) {
897 fGeoData[0].fColor = GrColor_ILLEGAL;
898 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
899 fGeoData[0].fColor = init.fOverrideColor;
900 }
901
902 // setup batch properties
903 fBatch.fColorIgnored = init.fColorIgnored;
904 fBatch.fColor = fGeoData[0].fColor;
905 fBatch.fStroke = fGeoData[0].fStroke;
906 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
907 fBatch.fCoverageIgnored = init.fCoverageIgnored;
908 }
909
mtklein36352bf2015-03-25 18:17:31 -0700910 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800911 SkMatrix invert;
912 if (!this->viewMatrix().invert(&invert)) {
913 return;
914 }
915
916 // Setup geometry processor
917 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
918 this->stroke(),
919 invert));
joshualitt76e7fb62015-02-11 08:52:27 -0800920
921 batchTarget->initDraw(gp, pipeline);
922
923 // TODO this is hacky, but the only way we have to initialize the GP is to use the
924 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
925 // everywhere we can remove this nastiness
926 GrPipelineInfo init;
927 init.fColorIgnored = fBatch.fColorIgnored;
928 init.fOverrideColor = GrColor_ILLEGAL;
929 init.fCoverageIgnored = fBatch.fCoverageIgnored;
930 init.fUsesLocalCoords = this->usesLocalCoords();
931 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
932
933 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700934 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800935 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800936 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700937 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
938 helper.init(batchTarget, vertexStride, instanceCount));
939 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800940 return;
941 }
942
bsalomon8415abe2015-05-04 11:41:41 -0700943 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700944 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700945
bsalomonb5238a72015-05-05 07:49:49 -0700946 SkScalar xRadius = geom.fXRadius;
947 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800948
949 // Compute the reciprocals of the radii here to save time in the shader
950 SkScalar xRadRecip = SkScalarInvert(xRadius);
951 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700952 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
953 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800954
bsalomonb5238a72015-05-05 07:49:49 -0700955 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800956
957 // The inner radius in the vertex data must be specified in normalized space.
958 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
959 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
960 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
961 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
962
963 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
964 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
965 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
966 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
967
968 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
969 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
970 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
971 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
972
973 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
974 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
975 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
976 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
977
bsalomonb5238a72015-05-05 07:49:49 -0700978 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800979 }
bsalomone64eb572015-05-07 11:35:55 -0700980 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -0800981 }
982
983 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
984
985private:
986 EllipseBatch(const Geometry& geometry) {
987 this->initClassID<EllipseBatch>();
988 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700989
990 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800991 }
992
mtklein36352bf2015-03-25 18:17:31 -0700993 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800994 EllipseBatch* that = t->cast<EllipseBatch>();
995
996 // TODO use vertex color to avoid breaking batches
997 if (this->color() != that->color()) {
998 return false;
999 }
1000
1001 if (this->stroke() != that->stroke()) {
1002 return false;
1003 }
1004
1005 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1006 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1007 return false;
1008 }
1009
1010 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001011 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001012 return true;
1013 }
1014
1015 GrColor color() const { return fBatch.fColor; }
1016 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1017 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1018 bool stroke() const { return fBatch.fStroke; }
1019
1020 struct BatchTracker {
1021 GrColor fColor;
1022 bool fStroke;
1023 bool fUsesLocalCoords;
1024 bool fColorIgnored;
1025 bool fCoverageIgnored;
1026 };
1027
joshualitt76e7fb62015-02-11 08:52:27 -08001028 BatchTracker fBatch;
1029 SkSTArray<1, Geometry, true> fGeoData;
1030};
1031
joshualitt3e708c52015-04-30 13:49:27 -07001032static GrBatch* create_ellipse_batch(GrColor color,
1033 const SkMatrix& viewMatrix,
1034 bool useCoverageAA,
1035 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001036 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001037#ifdef SK_DEBUG
1038 {
1039 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001040 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001041 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001042 }
1043#endif
1044
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001045 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001046 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001047 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001048 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1049 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001050 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1051 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1052 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1053 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001054
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001055 // do (potentially) anisotropic mapping of stroke
1056 SkVector scaledStroke;
1057 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001058 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1059 viewMatrix[SkMatrix::kMSkewY]));
1060 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1061 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001062
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001063 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001064 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1065 SkStrokeRec::kHairline_Style == style;
1066 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001067
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001068 SkScalar innerXRadius = 0;
1069 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001070 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001071 if (SkScalarNearlyZero(scaledStroke.length())) {
1072 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1073 } else {
1074 scaledStroke.scale(SK_ScalarHalf);
1075 }
1076
1077 // we only handle thick strokes for near-circular ellipses
1078 if (scaledStroke.length() > SK_ScalarHalf &&
1079 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001080 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001081 }
1082
1083 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1084 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1085 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001086 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001087 }
1088
1089 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001090 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001091 innerXRadius = xRadius - scaledStroke.fX;
1092 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001093 }
1094
1095 xRadius += scaledStroke.fX;
1096 yRadius += scaledStroke.fY;
1097 }
1098
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001099 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001100 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001101 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001102 xRadius += SK_ScalarHalf;
1103 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001104
joshualitt76e7fb62015-02-11 08:52:27 -08001105 EllipseBatch::Geometry geometry;
1106 geometry.fViewMatrix = viewMatrix;
1107 geometry.fColor = color;
1108 geometry.fXRadius = xRadius;
1109 geometry.fYRadius = yRadius;
1110 geometry.fInnerXRadius = innerXRadius;
1111 geometry.fInnerYRadius = innerYRadius;
1112 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001113 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1114 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001115
joshualitt3e708c52015-04-30 13:49:27 -07001116 return EllipseBatch::Create(geometry);
1117}
1118
1119bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1120 GrPipelineBuilder* pipelineBuilder,
1121 GrColor color,
1122 const SkMatrix& viewMatrix,
1123 bool useCoverageAA,
1124 const SkRect& ellipse,
1125 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001126 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001127 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001128 if (!batch) {
1129 return false;
1130 }
1131
joshualitt99c7c072015-05-01 13:43:30 -07001132 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001133 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001134}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001135
joshualitt76e7fb62015-02-11 08:52:27 -08001136/////////////////////////////////////////////////////////////////////////////////////////////////
1137
1138class DIEllipseBatch : public GrBatch {
1139public:
1140 struct Geometry {
1141 GrColor fColor;
1142 SkMatrix fViewMatrix;
1143 SkScalar fXRadius;
1144 SkScalar fYRadius;
1145 SkScalar fInnerXRadius;
1146 SkScalar fInnerYRadius;
1147 SkScalar fGeoDx;
1148 SkScalar fGeoDy;
1149 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001150 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001151 };
1152
joshualitt99c7c072015-05-01 13:43:30 -07001153 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
1154 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
joshualitt76e7fb62015-02-11 08:52:27 -08001155 }
1156
mtklein36352bf2015-03-25 18:17:31 -07001157 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001158
mtklein36352bf2015-03-25 18:17:31 -07001159 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001160 // When this is called on a batch, there is only one geometry bundle
1161 out->setKnownFourComponents(fGeoData[0].fColor);
1162 }
mtklein36352bf2015-03-25 18:17:31 -07001163 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001164 out->setUnknownSingleComponent();
1165 }
1166
mtklein36352bf2015-03-25 18:17:31 -07001167 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001168 // Handle any color overrides
1169 if (init.fColorIgnored) {
1170 fGeoData[0].fColor = GrColor_ILLEGAL;
1171 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1172 fGeoData[0].fColor = init.fOverrideColor;
1173 }
1174
1175 // setup batch properties
1176 fBatch.fColorIgnored = init.fColorIgnored;
1177 fBatch.fColor = fGeoData[0].fColor;
1178 fBatch.fMode = fGeoData[0].fMode;
1179 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1180 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1181 }
1182
mtklein36352bf2015-03-25 18:17:31 -07001183 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001184 // Setup geometry processor
1185 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1186 this->viewMatrix(),
1187 this->mode()));
1188
joshualitt76e7fb62015-02-11 08:52:27 -08001189 batchTarget->initDraw(gp, pipeline);
1190
1191 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1192 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1193 // everywhere we can remove this nastiness
1194 GrPipelineInfo init;
1195 init.fColorIgnored = fBatch.fColorIgnored;
1196 init.fOverrideColor = GrColor_ILLEGAL;
1197 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1198 init.fUsesLocalCoords = this->usesLocalCoords();
1199 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1200
1201 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001202 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001203 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001204 QuadHelper helper;
1205 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1206 helper.init(batchTarget, vertexStride, instanceCount));
1207 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001208 return;
1209 }
1210
joshualitt76e7fb62015-02-11 08:52:27 -08001211 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001212 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001213
bsalomonb5238a72015-05-05 07:49:49 -07001214 SkScalar xRadius = geom.fXRadius;
1215 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001216
bsalomonb5238a72015-05-05 07:49:49 -07001217 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001218
1219 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001220 SkScalar offsetDx = geom.fGeoDx / xRadius;
1221 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001222
reed80ea19c2015-05-12 10:37:34 -07001223 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1224 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001225
1226 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1227 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1228 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1229
1230 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1231 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1232 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1233
1234 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1235 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1236 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1237
1238 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1239 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1240 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1241
bsalomonb5238a72015-05-05 07:49:49 -07001242 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001243 }
bsalomone64eb572015-05-07 11:35:55 -07001244 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001245 }
1246
1247 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1248
1249private:
joshualitt99c7c072015-05-01 13:43:30 -07001250 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
joshualitt76e7fb62015-02-11 08:52:27 -08001251 this->initClassID<DIEllipseBatch>();
1252 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001253
1254 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001255 }
1256
mtklein36352bf2015-03-25 18:17:31 -07001257 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001258 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1259
1260 // TODO use vertex color to avoid breaking batches
1261 if (this->color() != that->color()) {
1262 return false;
1263 }
1264
1265 if (this->mode() != that->mode()) {
1266 return false;
1267 }
1268
joshualittd96a67b2015-05-05 14:09:05 -07001269 // TODO rewrite to allow positioning on CPU
1270 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001271 return false;
1272 }
1273
1274 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001275 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001276 return true;
1277 }
1278
1279 GrColor color() const { return fBatch.fColor; }
1280 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1281 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1282 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1283
1284 struct BatchTracker {
1285 GrColor fColor;
1286 DIEllipseEdgeEffect::Mode fMode;
1287 bool fUsesLocalCoords;
1288 bool fColorIgnored;
1289 bool fCoverageIgnored;
1290 };
1291
joshualitt76e7fb62015-02-11 08:52:27 -08001292 BatchTracker fBatch;
1293 SkSTArray<1, Geometry, true> fGeoData;
1294};
1295
joshualitt3e708c52015-04-30 13:49:27 -07001296static GrBatch* create_diellipse_batch(GrColor color,
1297 const SkMatrix& viewMatrix,
1298 bool useCoverageAA,
1299 const SkRect& ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001300 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001301 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001302 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001303 SkScalar yRadius = SkScalarHalf(ellipse.height());
1304
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001305 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001306 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001307 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001308 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001309 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1310
1311 SkScalar innerXRadius = 0;
1312 SkScalar innerYRadius = 0;
1313 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1314 SkScalar strokeWidth = stroke.getWidth();
1315
1316 if (SkScalarNearlyZero(strokeWidth)) {
1317 strokeWidth = SK_ScalarHalf;
1318 } else {
1319 strokeWidth *= SK_ScalarHalf;
1320 }
1321
1322 // we only handle thick strokes for near-circular ellipses
1323 if (strokeWidth > SK_ScalarHalf &&
1324 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001325 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001326 }
1327
1328 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1329 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1330 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001331 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001332 }
1333
1334 // set inner radius (if needed)
1335 if (SkStrokeRec::kStroke_Style == style) {
1336 innerXRadius = xRadius - strokeWidth;
1337 innerYRadius = yRadius - strokeWidth;
1338 }
1339
1340 xRadius += strokeWidth;
1341 yRadius += strokeWidth;
1342 }
1343 if (DIEllipseEdgeEffect::kStroke == mode) {
1344 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1345 DIEllipseEdgeEffect::kFill;
1346 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001347
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001348 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001349 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1350 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1351 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1352 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001353 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1354 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001355
joshualitt76e7fb62015-02-11 08:52:27 -08001356 DIEllipseBatch::Geometry geometry;
1357 geometry.fViewMatrix = viewMatrix;
1358 geometry.fColor = color;
1359 geometry.fXRadius = xRadius;
1360 geometry.fYRadius = yRadius;
1361 geometry.fInnerXRadius = innerXRadius;
1362 geometry.fInnerYRadius = innerYRadius;
1363 geometry.fGeoDx = geoDx;
1364 geometry.fGeoDy = geoDy;
1365 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001366 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1367 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001368
joshualittd96a67b2015-05-05 14:09:05 -07001369 SkRect devBounds = geometry.fBounds;
1370 viewMatrix.mapRect(&devBounds);
1371 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001372}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001373
joshualitt3e708c52015-04-30 13:49:27 -07001374bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1375 GrPipelineBuilder* pipelineBuilder,
1376 GrColor color,
1377 const SkMatrix& viewMatrix,
1378 bool useCoverageAA,
1379 const SkRect& ellipse,
1380 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001381 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualittd96a67b2015-05-05 14:09:05 -07001382 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001383 if (!batch) {
1384 return false;
1385 }
joshualitt99c7c072015-05-01 13:43:30 -07001386 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001387 return true;
1388}
1389
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001390///////////////////////////////////////////////////////////////////////////////
1391
1392static const uint16_t gRRectIndices[] = {
1393 // corners
1394 0, 1, 5, 0, 5, 4,
1395 2, 3, 7, 2, 7, 6,
1396 8, 9, 13, 8, 13, 12,
1397 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001398
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001399 // edges
1400 1, 2, 6, 1, 6, 5,
1401 4, 5, 9, 4, 9, 8,
1402 6, 7, 11, 6, 11, 10,
1403 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001404
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001405 // center
1406 // we place this at the end so that we can ignore these indices when rendering stroke-only
1407 5, 6, 10, 5, 10, 9
1408};
1409
joshualitt5ead6da2014-10-22 16:00:29 -07001410static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1411static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1412static const int kVertsPerRRect = 16;
1413static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001414
bsalomoned0bcad2015-05-04 10:36:42 -07001415GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1416GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1417static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1418 GrResourceProvider* resourceProvider) {
1419 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1420 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1421 if (strokeOnly) {
1422 return resourceProvider->refOrCreateInstancedIndexBuffer(
1423 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1424 gStrokeRRectOnlyIndexBufferKey);
1425 } else {
1426 return resourceProvider->refOrCreateInstancedIndexBuffer(
1427 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1428 gRRectOnlyIndexBufferKey);
1429
1430 }
1431}
1432
joshualitt9853cce2014-11-17 14:22:48 -08001433bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001434 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001435 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001436 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001437 bool useAA,
1438 const SkRRect& origOuter,
1439 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001440 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001441 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001442 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001443 if (!origInner.isEmpty()) {
1444 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001445 if (!viewMatrix.isIdentity()) {
1446 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001447 return false;
1448 }
1449 }
joshualittb0a8a372014-09-23 09:50:21 -07001450 GrPrimitiveEdgeType edgeType = applyAA ?
1451 kInverseFillAA_GrProcessorEdgeType :
1452 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001453 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001454 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1455 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001456 return false;
1457 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001458 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001459 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001460 }
1461
1462 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001463 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001464 return true;
1465 }
1466
1467 SkASSERT(!origOuter.isEmpty());
1468 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001469 if (!viewMatrix.isIdentity()) {
1470 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001471 return false;
1472 }
1473 }
joshualittb0a8a372014-09-23 09:50:21 -07001474 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001475 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001476 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001477 if (NULL == effect) {
1478 return false;
1479 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001480 if (!arfp.isSet()) {
1481 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001482 }
joshualittd27f73e2014-12-29 07:43:36 -08001483
1484 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001485 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001486 return false;
1487 }
joshualittd27f73e2014-12-29 07:43:36 -08001488
egdaniel8dd688b2015-01-22 10:16:09 -08001489 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001490 SkRect bounds = outer->getBounds();
1491 if (applyAA) {
1492 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1493 }
egdaniel8dd688b2015-01-22 10:16:09 -08001494 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001495 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001496}
1497
joshualitt76e7fb62015-02-11 08:52:27 -08001498///////////////////////////////////////////////////////////////////////////////////////////////////
1499
1500class RRectCircleRendererBatch : public GrBatch {
1501public:
1502 struct Geometry {
1503 GrColor fColor;
1504 SkMatrix fViewMatrix;
1505 SkScalar fInnerRadius;
1506 SkScalar fOuterRadius;
1507 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001508 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001509 };
1510
bsalomoned0bcad2015-05-04 10:36:42 -07001511 static GrBatch* Create(const Geometry& geometry) {
1512 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001513 }
1514
mtklein36352bf2015-03-25 18:17:31 -07001515 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001516
mtklein36352bf2015-03-25 18:17:31 -07001517 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001518 // When this is called on a batch, there is only one geometry bundle
1519 out->setKnownFourComponents(fGeoData[0].fColor);
1520 }
mtklein36352bf2015-03-25 18:17:31 -07001521 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001522 out->setUnknownSingleComponent();
1523 }
1524
mtklein36352bf2015-03-25 18:17:31 -07001525 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001526 // Handle any color overrides
1527 if (init.fColorIgnored) {
1528 fGeoData[0].fColor = GrColor_ILLEGAL;
1529 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1530 fGeoData[0].fColor = init.fOverrideColor;
1531 }
1532
1533 // setup batch properties
1534 fBatch.fColorIgnored = init.fColorIgnored;
1535 fBatch.fColor = fGeoData[0].fColor;
1536 fBatch.fStroke = fGeoData[0].fStroke;
1537 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1538 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1539 }
1540
mtklein36352bf2015-03-25 18:17:31 -07001541 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001542 // reset to device coordinates
1543 SkMatrix invert;
1544 if (!this->viewMatrix().invert(&invert)) {
1545 SkDebugf("Failed to invert\n");
1546 return;
1547 }
1548
1549 // Setup geometry processor
1550 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1551 this->stroke(),
1552 invert));
1553
1554 batchTarget->initDraw(gp, pipeline);
1555
1556 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1557 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1558 // everywhere we can remove this nastiness
1559 GrPipelineInfo init;
1560 init.fColorIgnored = fBatch.fColorIgnored;
1561 init.fOverrideColor = GrColor_ILLEGAL;
1562 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1563 init.fUsesLocalCoords = this->usesLocalCoords();
1564 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1565
1566 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001567 size_t vertexStride = gp->getVertexStride();
1568 SkASSERT(vertexStride == sizeof(CircleVertex));
1569
bsalomonb5238a72015-05-05 07:49:49 -07001570 // drop out the middle quad if we're stroked
1571 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001572 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1573 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001574
bsalomonb5238a72015-05-05 07:49:49 -07001575 InstancedHelper helper;
1576 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget,
1577 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1578 indicesPerInstance, instanceCount));
1579 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001580 SkDebugf("Could not allocate vertices\n");
1581 return;
1582 }
1583
joshualitt76e7fb62015-02-11 08:52:27 -08001584 for (int i = 0; i < instanceCount; i++) {
1585 Geometry& args = fGeoData[i];
1586
1587 SkScalar outerRadius = args.fOuterRadius;
1588
egdanielbc227142015-04-21 06:28:08 -07001589 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001590
1591 SkScalar yCoords[4] = {
1592 bounds.fTop,
1593 bounds.fTop + outerRadius,
1594 bounds.fBottom - outerRadius,
1595 bounds.fBottom
1596 };
1597
1598 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1599 // The inner radius in the vertex data must be specified in normalized space.
1600 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1601 for (int i = 0; i < 4; ++i) {
1602 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1603 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1604 verts->fOuterRadius = outerRadius;
1605 verts->fInnerRadius = innerRadius;
1606 verts++;
1607
1608 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1609 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1610 verts->fOuterRadius = outerRadius;
1611 verts->fInnerRadius = innerRadius;
1612 verts++;
1613
1614 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1615 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1616 verts->fOuterRadius = outerRadius;
1617 verts->fInnerRadius = innerRadius;
1618 verts++;
1619
1620 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1621 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1622 verts->fOuterRadius = outerRadius;
1623 verts->fInnerRadius = innerRadius;
1624 verts++;
1625 }
1626 }
1627
bsalomone64eb572015-05-07 11:35:55 -07001628 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001629 }
1630
1631 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1632
1633private:
bsalomoned0bcad2015-05-04 10:36:42 -07001634 RRectCircleRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001635 this->initClassID<RRectCircleRendererBatch>();
1636 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001637
1638 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001639 }
1640
mtklein36352bf2015-03-25 18:17:31 -07001641 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001642 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1643
1644 // TODO use vertex color to avoid breaking batches
1645 if (this->color() != that->color()) {
1646 return false;
1647 }
1648
1649 if (this->stroke() != that->stroke()) {
1650 return false;
1651 }
1652
1653 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1654 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1655 return false;
1656 }
1657
1658 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001659 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001660 return true;
1661 }
1662
1663 GrColor color() const { return fBatch.fColor; }
1664 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1665 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1666 bool stroke() const { return fBatch.fStroke; }
1667
1668 struct BatchTracker {
1669 GrColor fColor;
1670 bool fStroke;
1671 bool fUsesLocalCoords;
1672 bool fColorIgnored;
1673 bool fCoverageIgnored;
1674 };
1675
joshualitt76e7fb62015-02-11 08:52:27 -08001676 BatchTracker fBatch;
1677 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001678};
1679
1680class RRectEllipseRendererBatch : public GrBatch {
1681public:
1682 struct Geometry {
1683 GrColor fColor;
1684 SkMatrix fViewMatrix;
1685 SkScalar fXRadius;
1686 SkScalar fYRadius;
1687 SkScalar fInnerXRadius;
1688 SkScalar fInnerYRadius;
1689 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001690 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001691 };
1692
bsalomoned0bcad2015-05-04 10:36:42 -07001693 static GrBatch* Create(const Geometry& geometry) {
1694 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001695 }
1696
mtklein36352bf2015-03-25 18:17:31 -07001697 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001698
mtklein36352bf2015-03-25 18:17:31 -07001699 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001700 // When this is called on a batch, there is only one geometry bundle
1701 out->setKnownFourComponents(fGeoData[0].fColor);
1702 }
mtklein36352bf2015-03-25 18:17:31 -07001703 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001704 out->setUnknownSingleComponent();
1705 }
1706
mtklein36352bf2015-03-25 18:17:31 -07001707 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001708 // Handle any color overrides
1709 if (init.fColorIgnored) {
1710 fGeoData[0].fColor = GrColor_ILLEGAL;
1711 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1712 fGeoData[0].fColor = init.fOverrideColor;
1713 }
1714
1715 // setup batch properties
1716 fBatch.fColorIgnored = init.fColorIgnored;
1717 fBatch.fColor = fGeoData[0].fColor;
1718 fBatch.fStroke = fGeoData[0].fStroke;
1719 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1720 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1721 }
1722
mtklein36352bf2015-03-25 18:17:31 -07001723 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001724 // reset to device coordinates
1725 SkMatrix invert;
1726 if (!this->viewMatrix().invert(&invert)) {
1727 SkDebugf("Failed to invert\n");
1728 return;
1729 }
1730
1731 // Setup geometry processor
1732 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1733 this->stroke(),
1734 invert));
1735
1736 batchTarget->initDraw(gp, pipeline);
1737
1738 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1739 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1740 // everywhere we can remove this nastiness
1741 GrPipelineInfo init;
1742 init.fColorIgnored = fBatch.fColorIgnored;
1743 init.fOverrideColor = GrColor_ILLEGAL;
1744 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1745 init.fUsesLocalCoords = this->usesLocalCoords();
1746 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1747
1748 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001749 size_t vertexStride = gp->getVertexStride();
1750 SkASSERT(vertexStride == sizeof(EllipseVertex));
1751
bsalomonb5238a72015-05-05 07:49:49 -07001752 // drop out the middle quad if we're stroked
1753 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001754 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
1755 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001756
bsalomonb5238a72015-05-05 07:49:49 -07001757 InstancedHelper helper;
1758 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1759 helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
1760 kVertsPerRRect, indicesPerInstance, instanceCount));
1761 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001762 SkDebugf("Could not allocate vertices\n");
1763 return;
1764 }
1765
joshualitt76e7fb62015-02-11 08:52:27 -08001766 for (int i = 0; i < instanceCount; i++) {
1767 Geometry& args = fGeoData[i];
1768
1769 // Compute the reciprocals of the radii here to save time in the shader
1770 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1771 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1772 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1773 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1774
1775 // Extend the radii out half a pixel to antialias.
1776 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1777 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1778
egdanielbc227142015-04-21 06:28:08 -07001779 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001780
1781 SkScalar yCoords[4] = {
1782 bounds.fTop,
1783 bounds.fTop + yOuterRadius,
1784 bounds.fBottom - yOuterRadius,
1785 bounds.fBottom
1786 };
1787 SkScalar yOuterOffsets[4] = {
1788 yOuterRadius,
1789 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1790 SK_ScalarNearlyZero,
1791 yOuterRadius
1792 };
1793
1794 for (int i = 0; i < 4; ++i) {
1795 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1796 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1797 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1798 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1799 verts++;
1800
1801 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1802 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1803 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1804 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1805 verts++;
1806
1807 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1808 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1809 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1810 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1811 verts++;
1812
1813 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1814 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1815 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1816 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1817 verts++;
1818 }
1819 }
bsalomone64eb572015-05-07 11:35:55 -07001820 helper.issueDraw(batchTarget);
joshualitt76e7fb62015-02-11 08:52:27 -08001821 }
1822
1823 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1824
1825private:
bsalomoned0bcad2015-05-04 10:36:42 -07001826 RRectEllipseRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001827 this->initClassID<RRectEllipseRendererBatch>();
1828 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001829
1830 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001831 }
1832
mtklein36352bf2015-03-25 18:17:31 -07001833 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001834 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1835
1836 // TODO use vertex color to avoid breaking batches
1837 if (this->color() != that->color()) {
1838 return false;
1839 }
1840
1841 if (this->stroke() != that->stroke()) {
1842 return false;
1843 }
1844
1845 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1846 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1847 return false;
1848 }
1849
1850 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001851 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001852 return true;
1853 }
1854
1855 GrColor color() const { return fBatch.fColor; }
1856 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1857 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1858 bool stroke() const { return fBatch.fStroke; }
1859
1860 struct BatchTracker {
1861 GrColor fColor;
1862 bool fStroke;
1863 bool fUsesLocalCoords;
1864 bool fColorIgnored;
1865 bool fCoverageIgnored;
1866 };
1867
joshualitt76e7fb62015-02-11 08:52:27 -08001868 BatchTracker fBatch;
1869 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001870};
1871
joshualitt3e708c52015-04-30 13:49:27 -07001872static GrBatch* create_rrect_batch(GrColor color,
1873 const SkMatrix& viewMatrix,
1874 const SkRRect& rrect,
joshualittd96a67b2015-05-05 14:09:05 -07001875 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001876 SkASSERT(viewMatrix.rectStaysRect());
1877 SkASSERT(rrect.isSimple());
1878 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001879
joshualitt3e708c52015-04-30 13:49:27 -07001880 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001881 // do any matrix crunching before we reset the draw state for device coords
1882 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001883 SkRect bounds;
1884 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001885
1886 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001887 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1888 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1889 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1890 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001891
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001892 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001893
1894 // do (potentially) anisotropic mapping of stroke
1895 SkVector scaledStroke;
1896 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001897
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001898 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1899 SkStrokeRec::kHairline_Style == style;
1900 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1901
1902 if (hasStroke) {
1903 if (SkStrokeRec::kHairline_Style == style) {
1904 scaledStroke.set(1, 1);
1905 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001906 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1907 viewMatrix[SkMatrix::kMSkewY]));
1908 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1909 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001910 }
1911
1912 // if half of strokewidth is greater than radius, we don't handle that right now
1913 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001914 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001915 }
1916 }
1917
1918 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1919 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1920 // patch will have fractional coverage. This only matters when the interior is actually filled.
1921 // We could consider falling back to rect rendering here, since a tiny radius is
1922 // indistinguishable from a square corner.
1923 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001924 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001925 }
1926
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001927 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001928 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001929 SkScalar innerRadius = 0.0f;
1930 SkScalar outerRadius = xRadius;
1931 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001932 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001933 if (SkScalarNearlyZero(scaledStroke.fX)) {
1934 halfWidth = SK_ScalarHalf;
1935 } else {
1936 halfWidth = SkScalarHalf(scaledStroke.fX);
1937 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001938
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001939 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001940 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001941 }
1942 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001943 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001944 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001945
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001946 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001947
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001948 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001949 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1950 // Second, the outer radius is used to compute the verts of the bounding box that is
1951 // rendered and the outset ensures the box will cover all partially covered by the rrect
1952 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001953 outerRadius += SK_ScalarHalf;
1954 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001955
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001956 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001957 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001958
joshualitt76e7fb62015-02-11 08:52:27 -08001959 RRectCircleRendererBatch::Geometry geometry;
1960 geometry.fViewMatrix = viewMatrix;
1961 geometry.fColor = color;
1962 geometry.fInnerRadius = innerRadius;
1963 geometry.fOuterRadius = outerRadius;
1964 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001965 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001966
bsalomoned0bcad2015-05-04 10:36:42 -07001967 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001968 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001969 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001970 SkScalar innerXRadius = 0.0f;
1971 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001972 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001973 if (SkScalarNearlyZero(scaledStroke.length())) {
1974 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1975 } else {
1976 scaledStroke.scale(SK_ScalarHalf);
1977 }
1978
1979 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001980 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001981 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001982 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001983 }
1984
1985 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1986 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1987 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001988 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001989 }
1990
1991 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001992 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001993 innerXRadius = xRadius - scaledStroke.fX;
1994 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001995 }
1996
1997 xRadius += scaledStroke.fX;
1998 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001999 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002000 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002001
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002002 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002003
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002004 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07002005 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002006
joshualitt76e7fb62015-02-11 08:52:27 -08002007 RRectEllipseRendererBatch::Geometry geometry;
2008 geometry.fViewMatrix = viewMatrix;
2009 geometry.fColor = color;
2010 geometry.fXRadius = xRadius;
2011 geometry.fYRadius = yRadius;
2012 geometry.fInnerXRadius = innerXRadius;
2013 geometry.fInnerYRadius = innerYRadius;
2014 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002015 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002016
bsalomoned0bcad2015-05-04 10:36:42 -07002017 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002018 }
joshualitt3e708c52015-04-30 13:49:27 -07002019}
2020
2021bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2022 GrPipelineBuilder* pipelineBuilder,
2023 GrColor color,
2024 const SkMatrix& viewMatrix,
2025 bool useAA,
2026 const SkRRect& rrect,
2027 const SkStrokeRec& stroke) {
2028 if (rrect.isOval()) {
2029 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2030 stroke);
2031 }
2032
2033 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2034
2035 // only anti-aliased rrects for now
2036 if (!useCoverageAA) {
2037 return false;
2038 }
2039
2040 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2041 return false;
2042 }
2043
joshualittd96a67b2015-05-05 14:09:05 -07002044 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002045 if (!batch) {
2046 return false;
2047 }
2048
joshualitt99c7c072015-05-01 13:43:30 -07002049 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002050 return true;
2051}
joshualitt3e708c52015-04-30 13:49:27 -07002052
2053///////////////////////////////////////////////////////////////////////////////////////////////////
2054
2055#ifdef GR_TEST_UTILS
2056
joshualitt3e708c52015-04-30 13:49:27 -07002057BATCH_TEST_DEFINE(CircleBatch) {
2058 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2059 GrColor color = GrRandomColor(random);
2060 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002061 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002062 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2063 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002064}
2065
2066BATCH_TEST_DEFINE(EllipseBatch) {
2067 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2068 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002069 SkRect ellipse = GrTest::TestSquare(random);
2070 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002071 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002072}
2073
2074BATCH_TEST_DEFINE(DIEllipseBatch) {
2075 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2076 GrColor color = GrRandomColor(random);
2077 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002078 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002079 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002080 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002081}
2082
2083BATCH_TEST_DEFINE(RRectBatch) {
2084 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2085 GrColor color = GrRandomColor(random);
2086 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002087 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002088}
2089
2090#endif